1 JAVA基本数据类型
1、整数型
byte、short、int、long
2、浮点型
float、double
3、字符型
char
4、布尔型
boolean
JAVA引用数据类型
字符串、数组、类、接口、Lambda
注意事项
- 字符串不是基本类型,而是引用类型
- 浮点型可能只是一个近似值,而并非精确的值
- 数据范围与字节数不一定相关,例如float数据范围比long更广泛,但是float是4字节,long是8字节
- 浮点数当中默认类型是double。如果一定要使用float类型,则需要加上一个后缀F
- 整数默认为int类型,如果一定要使用long类型,则需要加上一个后缀L
2 变量的概念与格式
定义一个变量:
// 数据类型 变量名称;
// 变量名称 = 数据值;
public class DemoVariable{
public static void main(String[] args){
int num1;
num1 = 1;
int num2 = 2;
//下面是错误的赋值方式
long num3 = 10000000000;
//正确应该改为
long num3 = 10000000000L;
float num4 = 2.5F;
double num5 = 1.5;
char zifu1 = 'A';
boolean var1 = true;
boolean var2 = var1;
}
}
使用变量的注意事项
- 如果创建多个变量,那么变量之间的名称不可以重复(不能
int
重复的变量) - 对于float和long类型来说,字母后缀的F与L别丢了
- 如果使用byte或者short类型,则千万别超范围了
- 变量要先赋值才能使用
- 变量使用不能超过作用域的范围(定义需要在调用之前的行上,大括号也能弄出一个作用域)
局部变量与成员变量区别
1 定义的位置不一样
局部变量:在方法的内部(方法范围)
成员变量:在方法的外部,直接写在类当中(类的范围)
2 作用范围不一样
局部变量:只有方法当中才可以使用,出了方法就不能够再使用
成员变量:整个类全部都可以使用
3 默认值不一样
局部变量:没有默认值,如果想要使用,必须手动进行赋值
成员变量:如果没有赋值,默认会有一个值
4 内存的位置不一样
局部变量:位于栈中
成员变量:位于堆中(new
出来的)
5 生命周期不一样
局部变量:随着方法进栈而诞生,随着方法出栈而消失
成员变量:随着对象创建而诞生,随着对象被垃圾回收而消失
3 JAVA数据类型转换
自动类型转换(隐式)
- 特点:代码不需要进行特殊处理,自动完成
- 规则:数据范围从小到大
public class Demo01Datatype{
public static void main(String[] args){
long num1 = 100;
// 左边是long类型,右边是默认的int类型
// int ——> long 满足从小到大的要求
// 发生了隐式类型转换
double num2 = 2.5F;
// float --> double 从小到大满足
float num3 = 30L;
// long --> float 同样满足
}
}
强制类型转换
- 代码需要进行特殊的格式处理,不能自动完成
- 格式: 范围小的类型 范围小的变量名 = (范围小的类型) 原本范围大的数据
- 比自动多了一个小括号
public class Demo02Datatype{
public static void main(String[] args){
int num1 = (int) 100L;
//long类型强制转换为int类型
}
}
注意事项
- 强制类型转换一般不推荐使用,因为有可能发生精度损失、数据溢出
- boolean类型不能发生强制类型转换
4 JAVA运算符
四则运算
四则运算当中的加号'+'有常见的三种用法
- 对于数值来说,就是普通的加法
- 对于
char
类型来说,在计算之前,char
会被提升为int
再计算 对于
String
来说,加号代表字符串连接操作- 对于任何数据类型和字符串进行连接的时候,结果都会变成字符串
赋值运算符
基本赋值运算符就是一个等于号
复合赋值运算符有5种,和python一样
+=, -=, *=, /=, %=
比较运算符
大于小于大于等于小于等于……没啥好写的
逻辑运算符
与&& 或|| 非!
没啥好写的
三元运算符
一元运算符:只需要一个数据就可以进行操作的运算符
- 例如取反、自增、自减
二元运算符:需要两个数据
- 例如加法、赋值
三元运算符:
格式:数据类型 = 条件判断 ? 表达式A : 表达式B
流程:首先判断条件是否成立
如果为true
则将表达式A的赋值给左边的变量;
如果为false
则将表达式B的值赋值给左边的变量。
public class DemoOperator{
public static void main(String[] args){
int a=10, b=20;
int max = a>b ? a:b;
}
}
注意事项:
- 必须同时保证表达式A和表达式B都符合左侧数据类型的要求。
//例如不可以是
int result = 3>4 ? 2.5:10;
//2.5不满足int,所以不能这样写
System.out.println(a>b?a:b);
//上面为正确写法
5 选择语句
主要是switch相关的
public class DemoSwitch{
public static void main(String[] args){
int num = 1;
switch (num) {
case 1:
System.out.println("Monday");
break;
case 2:
System.out.println("OtherDay");
break;
default:
System.out.println("随便什么day");
break;
}
}
}
switch语句的注意事项
- 多个case后面的数值不可以重复
switch语句后面小括号当中只能是以下的数据类型
- 基本数据类型:byte/short/char/int
- 引用数据类型:String/enum
- switch语句格式可以很灵活,前后顺序可以颠倒,而且break语句还可以省略(仅最后,且不建议)
6 循环语句
For循环
For循环语句格式:
for (初始化表达式1; 布尔表达式2; 步进表达式3){
循环体;
}
While循环
While循环格式:
初始化表达式1;
while(布尔表达式2){
循环体3;
步进表达式4;
}
Do-while循环
do{
循环体;
步进语句;
} while(条件判断(boolean));
break语句
- 可以用在switch语句当中,一旦执行,整个switch语句立刻结束。
- 可以用在循环语句当中,一旦执行,整个循环语句立刻结束,打断循环。
continue关键字
一旦执行,立刻跳过当前循环语句剩余内容,马上开始下一次循环。
7 JAVA方法
方法使用的注意事项:
- 方法应该定义在类之中,不能在方法之中再定义方法。
- 方法定义前后顺序无所谓
- 方法定义之后不会执行,如果要执行则必须要调用(same as python)
- 如果有返回值那么必须要写return
- return的返回值数据必须要和方法返回值类型一样
- 如果是void类型的方法可以只写一个return,当然也可以不写
方法重载
简而言之:可以定义多个方法名称一样但是参数列表不一样的方法
public class Demo01MO{
public static void main(String[] args){
System.out.println(sum(10, 20));
System.out.println(sum(10, 20, 30));
}
public static int sum(int a, int b){
return a + b;
}
public static int sum(int a, int b, int c){
return a + b + c;
}
}
重载相关tips
可以重载的类型
- 参数个数不同
- 参数类型不同
- 参数的多类型顺序不同(传入参数可以是不同类型)
不能重载的类型
- 名称一样,参数一样,返回值不一样(与返回值无关)
- 与参数的名称也无关
- 和参数的static public属性也无关
简而言之,重载只看参数名称(是否一样)和参数(是否不一样)
println
是最常见的方法重载应用
8 JAVA数组
数组的概念:
数组是一种容器,可以同时存放多个数据值。
数组的特点:
- 数组是一种引用数据类型
- 数组当中的多个数据,类型必须统一
- 数组的长度在程序运行期间不可以改变
1.数组的初始化
两种常见的初始化方式:
- 动态初始化(指定长度)
- 静态初始化(指定内容)
动态初始化的基本格式
//数据类型[] 数组名称 = new 数据类型[数组长度]
int[] arrayA = new int[100];
double[] arrayB = new double[10];
String[] arrayC = new String[5];
//动态初始化时,整数类型默认为0,浮点类型为0.0,字符类型为'\u0000',引用类型为null,布尔为false
解析含义:
左侧数据类型:也就是数组当中保存的数据,全都是统一的什么类型
左侧的中括号:代表我是一个数组
左侧数组名称:给数组取一个名字
右侧的new:代表创建数组的动作
右侧数据类型:必须和左侧数据类型保持一致
右侧括号的长度:int
类型,代表可以保存的数据量
静态初始化
//数据类型[] 数组名称 = new 数据类型[] {元素1, 元素2, 元素3, .... };
int[] arrayA = new int[] {5, 15, 25};
String[] arrayB = new String[] {"hello", "world", "java"};
虽然没有指明长度,但是java会自己推算出长度。
静态初始化还可以有省略格式
int[] arrayA = {5, 15, 25};
int[] arrayB = {"hello", "world", "java"}
注意事项
- 静态初始化也是有长度的
- 静态初始化标准格式可以拆分为两个步骤
- 动态初始化也可以拆分成为两个步骤
- 静态初始化使用省略格式就不能拆分成两个步骤了
使用建议
- 不确定数组当中的具体内容的时候使用动态
- 否则使用静态初始化
2、数组元素的访问
直接println
数组名称,得到的是数组的内存地址的哈希值
int[] array = {10, 20, 30};
int num = array[0]; //按索引访问
array[1] = 123; //按索引赋值
3、数组的一些方法
获取数组的长度
int[] array = {......};
int len = array.length; // 使用array.length
System.out.println(len);
数组一旦创建,程序运行期间,长度不可以改变
数组的遍历输出
写个for
循环就能遍历
for(int i=0; i<array.length; i++){
System.out.println(array[i]);
}
IDEA简化版写法
数组名称.fori
就可以一键生成for循环遍历
数组作为返回值
一个方法可以有0,1,多个参数;但是只能有0或者1个返回值。
如果希望一个方法当中产生了多个结果数据进行返回,可以使用一个数组作为返回值类型。
public static int[] calculate(int a, int b, int c){
int sum = a + b + c;
int avg = sum / 3;
int[] array = { sum, avg };
return array;
}
4、对象数组
public class DemoArray{
public static void main(String[] args){
//创建一个长度为3的数组,用来存放Person类的对象
Person[] array = new Person[3];
Person one = new Person("mion", 22);
Person two = new Person("yuihan", 28);
Person three = new Person("yuiyui", 19);
//开始将数组进行赋值,赋的值为对象的内存地址
array[0] = one;
array[1] = two;
array[2] = three;
//开始调用
System.out.println(array[1].getName()); //返回yuihan
}
}
9 JAVA的内存划分
JAVA的内存划分为5个部分:
1、栈(stack)
存放的都是方法中的局部变量。
方法的运行一定要在栈中。
局部变量:方法的参数,或者是方法{}内部的变量
作用域:一旦超出作用域,立刻从栈内存当中消失
2、堆(heap)
凡是new出来的东西,都在堆当中
堆内存面的东西都有一个16进制的地址值
堆内存里面的东西都有一个默认值,其规则如下:
如果是整数 —— 默认为 0
如果是浮点数 —— 默认为 0.0
如果是字符 —— 默认为 '\u0000'
如果是布尔 —— 默认为 false
如果是引用类型 —— 默认为 null
3、方法区(method area)
储存.class相关信息,包含方法的信息。
4、本地方法栈(native method stack)
与OS相关
5、寄存器(pc register)
与CPU相关
数组与内存之间交互操作图
10 JAVA类与对象
public class Student{
String name;
int age;
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
public void study(){
System.out.println("学习");
}
}
成员变量处于类当中,方法之外;成员方法不需要static
关键字。
1、对象的创建与使用
通常情况下,一个类并不能直接使用,需要根据类创建一个对象,才能使用。
步骤:
1、导包
也就是刚指出需要使用的类,在什么位置。
import 包名称.类名称;
对于和当前类属于同一个包的情况,可以省略导包语句不写。
2、创建
类名称 对象名 = new 类名称();
Student stu1 = new Student();
3、使用
stu1.sleep();
stu1.study();
System.out.println(stu.name);
对象与内存划分
对于尚未实例化的对象,都储存在方法区
当代码执行,main
方法进栈,读取到实例化对象之后,生成一个对象,此时堆中出现new出来的对象同时保存属性值,并且保存这个类在方法区的方法地址,栈中保存堆中的对象内存地址(保存变量,但是方法只保存地址不保存具体的方法)
当修改对象属性的时候,按内存地址在堆中进行修改。
当执行对象方法的时候,首先在堆中拿到方法地址,再在方法区拿到方法,入栈执行该方法
方法执行完毕之后,出栈,继续执行栈中的main
方法,当main
方法执行结束后,出栈
使用对象当做方法的传入参数
public static void main(String[] args){
Phone one = new Phone();
one.brand = "苹果";
one.price = 5499;
one.color = "深空灰";
method(one); //one为一个对象传入
}
//给method方法传入的参数为名为Phone的类,记为param
public static void method(Phone param){
System.out.println(param.brand);
System.out.println(param.price);
System.out.println(param.color);
}
注意:当一个对象作为参数传入方法当中时,实际上传递过去的是对象的地址值。
使用对象当做方法的返回值
public static void main(String[] args){
Phone two = getPhone();
System.out.println(two.brand);
System.out.println(two.price);
System.out.println(two.color);
}
public static Phone getPhone(){
// 方法类型为Phone类型,名字为getPhone
Phone one = new Phone();
one.brand = "apple";
one.price = "6299";
one.color = "玫瑰金";
return one
}
返回过来的也是对象的内存地址。
2、构造一个标准的JAVA类
一个标准的类通常需要拥有下面四个组成部分:
- 所有的成员变量都要使用
private
关键字修饰 - 为每一个成员变量都编写一对
Getter
/Setter
方法 - 编写一个无参数的构造方法
- 编写一个全参数的构造方法
满足这样标准的类也叫作Java Bean
使用IDEA
IDE可以选择Code-Generate菜单中直接生成G/S方法
对于无参数的构造方法,同样也是在Code-Generate-Constructor中,选择select none
对于全参数的构造方法,就是在上面菜单中全选后点OK
public class Student{
private String name;
private int age;
}
//首先private关键字和属性还是要自己写的,写完后就可以用IDE生成其他的了
11 JAVA封装性
面向对象三大特性:封装、继承、多态
封装性在JAVA中的体现:
- 方法就是一种封装
- 关键字
private
也是一种封装
封装就是将一些细节信息隐藏起来,对于外界不可见。
private关键字
看一下使用private
关键字后实现对于类的属性的约束(提高代码安全性)
public class Person{
String name;
private int age;
public void show(){
System.out.println("my name is "+name+"aged"+age);
}
public void setAge(int num){
if(num<100 && num>=0) age=num;
else System.out.println("unvalid data");
}
}
public class DemoPerson{
public static void main(String[] args){
Person person = new Person();
person.name = "mion";
person.setAge(-20);
person.show();
}
}
注意事项:对于成员变量操作的方法名称必须叫getXxx
或者setXxx
对于Getter
来说,不能有参数,返回值类型和成员变量对应。
对于Setter
来说,不能有返回值,参数类型和成员变量对应。
对于boolean
值来说,Getter
方法一定要写成isXxx
的形式,而Setter
不变
this关键字
当方法的局部变量和类的成员变量重名的时候,根据就近原则,优先使用局部变量。
如果需要访问本类当中的成员变量,需要使用格式:
this.成员变量名
public class Person{
String name;
public void sayHello(String name){
System.out.println(name + "hello, i'm" + this.name)
}
}
通过类A调用的方法B,那么this
关键字就是指的类A
12 JAVA构造方法
什么是构造方法
java构造方法也叫构造函数,是java当中一类特殊的函数。函数名和类名相同,并且没有返回值。
作用:一般用来初始化成员属性和成员方法的,即使用new
产生对象后,就调用了对象所属类的属性和方法。
在现实生活中,很多事物一出现,就天生具有某些属性和行为。比如人一出生,就有年龄、身高、体重、就会哭;汽车一出产,就有颜色、有外观、可以运行等。这些,我们就可以将这些天然的属性和行为定义在构造函数中,当new
实例化对象时,也就具有这些属性和方法了,没必要再去重新定义了,从而加快了编程效率。
构造方法在对象建立的时候就会开始自己去运行,给对象赋予属性和方法
构造方法的特点
- 方法名和类名相同(大小写包含)
- 不用定义返回值类型,连
void
也不用写 - 不能写
return
语句 - 如果没有编写任何构造方法,那么编译器就会自带一个构造方法,没有参数,没有方法体。
- 一旦编写了至少一个构造方法,那么编译器将不会再自带构造方法。
- 构造方法也是可以进行重载的。
13 Scanner类
引用数据类型的一般使用步骤:
- 1、导包
import 包路径.类名称
如果需要使用的目标类和当前类位于同一个包下,则可以省略不写。
只有java.lang
包下的内容不需要导包,其他的包都需要import
- 2、创建
类名称 对象名 = new 类名称();
- 3、使用
对象名.成员方法名();
使用Scanner实现键盘输入
import java.util.Scanner; //导包
public class DemoScanner{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in); //创建
int num = scanner.nextInt(); //使用
String str = scanner.next(); //使用
System.out.println(num + str);
}
}
14 匿名对象
匿名对象就是只有new 类名称的对象,没有左边的名字和赋值运算符。
public class DemoAnonymous{
public static void main(String[] args){
// 标准对象创建与调用
Person one = new Person();
one.name = "mion";
one.showName(); //返回mion
// 匿名对象创建于调用
new Person().name = "yuihan"; //没有接收
new Person().showName(); //返回的是null
}
}
上面的例子总共创建了3个对象,匿名对象只有一次使用,下次就再也调用不到了
匿名对象的使用
一般使用:
int num = new Scanner(System.in).nextInt();
System.out.println("输入的是"+num);
//new+调用一气呵成
作为方法参数使用:
public static void main(String[] args){
methodParam(new Scanner(System.in));
}
public static void methodParam(Scanner i){
int num = i.nextInt();
System.out.println(num);
}
作为返回值
public static void main(String[] args){
Scanner sc = methodReturn();
int num = sc.nextInt();
System.out.println(num);
}
public static Scanner methodReturn(){
return new Scanner(System.in);
}
15 Random类
简单使用
import java.util.Random;
public class DemoRandom{
public static void main(String[] args){
Random r = new Random();
int num = r.nextInt();
System.out.println(num);
}
}
带区间的使用
int num = r.nextInt(3) //范围为0~2,3为开区间()所以取不到
16 ArrayList类
数组的长度不可以发生改变,但是ArrayList集合的长度是可以随意变化的。
对于ArrayList来说,有一个尖括号"<E>"代表泛型。
泛型:也就是装在集合当中的所有元素,全部都是统一的什么类型。
注意:泛型只能是引用类型,不能是基本类型。
public class DemoArrayList{
public static void main(String[] args){
//创建了一个ArrayList集合,集合的名称为list,里面装的都是String类型
//从JDK1.7开始右侧的<>内可以不用写内容
ArrayList<String> list = new ArrayList<>();
}
}
对于ArrayList来说,直接打印的到的不是地址值而是内容。
如果内容是空,得到的是空的中括号[]。
1 添加方法(add)
public class DemoArrayList{
public static void main(String[] args){
ArrayList<String> list = new ArrayList<>();
list.add("mion");
list.add("yuihan");
list.add("yuiyui");
System.out.println(list);
}
}
用起来特别像python里面的列表,打印的方式也类似列表,add
相当于append
2 其他方法
public boolean add(E e)
:向集合添加元素,参数类型和泛型一致。返回值代表是否成功
对于ArrayList来说,add动作一定是成功的,所以返回值可用可不用。
但是对于其他契合来说,add动作不一定成功
public E get(int index)
:按索引获取元素
public E remove(int index)
:按索引删除元素,返回值为删掉元素
public int size()
:获取集合的长度,返回元素个数
public class DemoArrayList{
public static void main(String[] args){
ArrayList<String> list = new ArrayList<>();
list.add("mion");
list.add("yuihan");
list.add("yuiyui");
System.out.println(list);
boolean success = list.add("yukirin"); //true
String whoRemoved = list.remove(3); //yukirin
String name = list.get(2); //yuiyui
int size = list.size(); //3
}
}
list.fori
也可以一键生成遍历for循环
如果想往ArrayList中储存基本数据类型的话
必须使用基本类型对应的包装类
基本类型 包装类(都位于java.lang下)
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
比如说
ArrayList<Integer> listI = new ArrayList<>();
listI.add(100);
从JDK 1.5开始,开始支持自动装箱和拆箱
17 String类
位于java.lang.String
中
Java程序中所有字符串字面值(如"abc")都作为此类的实例实现。所有的双引号字符串都是String类的实例化对象。
字符串的特点
- 字符串的内容永不可改变
- 正是因为字符串不可改变,所以字符串是可以共享使用的
- 字符串熬过上相当于是
char[]
字符数组,但是底层原理是byte[]
字节数组
创建字符串的常见3+1种方式
第一种
public String[]
构造一个空白字符串,不含有任何内容。
第二种
public String(char[] array)
根据字符数组的内容,来创建对应的字符串
第三种
public String(byte[] array)
根据字节数组的内容,来创建对应的字符串
最后一种是直接创建,就不说了
字符串常量池
程序当中直接写上的双引号字符串,就在字符串常量池中。
自己new
出来的对象就不会进入池子里面(如下面的例子)
对于基本类型来说,==
是进行数值的比较
对于引用类型来说,==
是进行地址值的比较
public static void main(String[] args){
String str1 = "abc";
String str2 = "abc";
char[] = charArray = {'a','b','c'};
String str3 = new String(charArray);
System.out.println(str1 == str2); //true
System.out.println(str1 == str3); //false
System.out.println(str2 == str3); //false
}
字符串的比较
==
是进行对象的地址值比较,如果确实需要字符串的内容比较,可以使用两个方法:
第一种:使用String
的equals
方法
public boolean equals(Object obj)
参数可以是任何对象,只有参数是一个字符串而且内容相同的才会给true
,否则返回false
。
任何对象都能用Object
进行接收。
String str1 = "abc";
String str2 = "abc";
char[] = charArray = {'a','b','c'};
String str3 = new String(charArray);
String str4 = "ABC";
System.out.println(str1.equals(str2)); //true
System.out.println(str1.equals(str3)); //true
System.out.println("abc".equals(str2)); //true
System.out.println(str1.equalsIgnoreCase(str4)); //true 不区分大小写的方法
字符串的其他方法
字符串的GET方法
public int length():获取字符串当中含有的字符个数,拿到字符串长度。
public String concat(String str):将当前字符串和参数字符串拼接成为返回新的字符串。
public char charAt(int index):根据索引获取值
public int indexOf(String str):根据值获取索引,如果没有返回-1
字符串的截取方法
public String substring(int index):从参数未知一直到字符串末尾,返回新字符串
public String substring(int begin, int end):左闭右开区间,从这个区间截取
字符串的转换方法
public char[] toCharArray():将字符串拆分成为字符数组作为返回值
public byte[] getBytes():获得当前字符串底层的字节数组
public String replace(CharSequence oldString, CharSequence newString):
将所有出现的字符串替换成为新的字符串,返回替换之后的新字符串。
字符串的分割方法
public String[] split(String regex):按照参数的规则,分割(就是普通的split),参数regex是正则表达式,返回值是一个String数组
18 JAVA静态
一旦使用了static
关键字,那么这样的内容不再属于对象自己,而是属于类,凡是本类的对象,都共享同一份内容。
public class MyClass{
public void method(){
System.out.println("这是一个成员方法");
}
public static void methodStatic(){
System.out.println("这是一个静态方法");
}
}
public class DemoStaticMethod{
public static void main(String[] args){
MyClass obj = new MyClass();
obj.method;
//对象名和类名都能调用静态方法
obj.methodStatic(); //虽然正确但不规范,静态成员方法应该直接通过类名调用而不是对象
MyClass.methodStatic(); //代码规范应该这样写,上面的经过javac翻译后就变成这样的
//如果方法在本类中,则可以直接调用
}
}
注意事项
1、静态不能直接访问非静态
因为在内存当中先有的静态内容,后有的非静态内容
public class MyClass{
int num;
static int numStatic;
public static void methodStatic(){
System.out.println(numStatic);
System.out.println(num); //报错,静态不能访问非静态
}
}
2、静态方法当中不能使用this
关键字
静态方法直接类调用非方法调用
静态的内存图
静态代码块
当第一次用到本类时,静态代码块执行唯一的一次
从来一次性地对静态成员变量进行赋值
public class Person{
static{
System.out.println("静态代码块执行!");
}
public Person(){
System.out.println("构造方法执行");
}
}
public class DemoStatic{
public static void main(String[] args){
Person one = new Person();
//静态代码块执行!
//构造方法执行
Person two = new Person();
//构造方法执行
}
}
19 Arrays与Math类
Arrays类
java.util.Arrays
是一个与数组相关的工具类,里面提供了大量的静态方法,用来实现数组常见的操作。
public static String toString(数组):将参数数组变成字符串(按照默认格式:[元素1,元素2,...])
public static void sort(数组):按照升序对数组进行排序
public class DemoArrays{
public static void main(String[] args){
int[] intArray = {4, 3, 2, 1,};
String intStr = Arrays.toString(intArray);
System.out.println(intStr); //[4, 3, 2, 1]
Arrays.sort(intArray);
System.out.println(Arrays.toString(intArray)); //[1, 2, 3, 4]
}
}
Math类
java.util.Math
是一个数学相关的工具类,里面提供了大量的静态方法,完成数学运算相关的操作。
public static double abs(double num): 取绝对值
public static double ceil(double num): 向上取整
public static double floor(double num):向下取整
public static long round(double num):四舍五入
Math.PI:代表近似的π(3.14159265358979323846)
20 JAVA继承
继承是多态的前提,如果没有继承就没有多态。
继承主要解决的问题就是:共性抽取
继承关系当中的特点:
- 子类可以拥有父类的"内容"
- 子类还可以拥有自己专有的内容
继承的格式:(一个普通的类定义)
public class 父类名称{
//....
}
public class 子类名称 extends 父类名称{
//....
}
对于继承当中三种重名变量的读取方法
//局部变量: 直接写变量名
//本类的成员变量: this.成员变量名
//父类的成员变量: super.成员变量名
public class Zi extends Fu{
int num = 20;
public void method(){
int num = 30;
System.out.println(num); //返回30
System.out.println(this.num); //返回20
System.out.println(super.num); //返回10,假设父类中定义为10
}
}
对于重名的成员方法的访问规则:
创建的对象是谁,则优先用谁,如果没有则向上找。就不写例子了。
继承中方法的覆盖重写
方法覆盖重写的注意事项:
- 必须保证父子类之间方法的名称相同,参数列表也相同。
- 使用
@Override
重写注解来检测是否是有效的正确覆盖重写 子类方法的返回值必须小于等于父类方法的返回值范围
- Ps:
java.lang.Object
类是所有类的公共最高父类 - 比如父类返回值为
Object
时,子类可以为String
,int
类
- Ps:
子类方法的权限必须大于等于父类方法的权限修饰符
- 关于权限修饰符: public > protected > (什么都不写) > private
例如下面就能构成一个方法覆盖重写
//父类
public class Fu {
public void method(){
//...
}
}
//子类
@Override
public class Zi extends Fu {
public void method{
//...
}
}
继承应用场景:设计原则——对于已经投入使用的类,尽量不要进行修改。推荐定义一个新的类,来重复利用其中共性内容,并且添加改动新内容。
继承中构造方法的访问特点
当子类继承父类后,执行实例化时,会先执行父类构造方法,再执行子类构造方法
- 子类构造方法当中有一个默认隐含的
super()
调用,所以才会先父后子。
- 子类构造方法当中有一个默认隐含的
- 当然也可以通过
super
关键字来子类构造调用父类重载,例如
//父类
public class Fu{
public Fu(){
System.out.println("父类无参构造");
}
public Fu(int num){
System.out.println("父类有参构造");
}
}
//子类
public class Zi extends Fu{
public Zi(){
super(20); //通过使用super关键字传参来调用父类的有参重载
System.out.println("abc");
}
}
super
的父类构造调用,必须是子类构造方法的第一个语句。- 只有子类构造方法才能调用父类构造方法
- 而且
super
必须要在子类构造方法的第一个语句中出现 - 不能一个子类构造调用多次
super
构造
- 总结:子类必然调用父类构造方法,不写的话会自带
super()
,写了的话按写了的调用。super
只能有一个,而且必须是第一个。
拓展:关于super关键字的三种用法
- 在子类的成员方法中,访问父类的成员变量
- 在子类的成员方法中,访问父类的成员方法
- 在子类的构造方法中,访问父类的构造方法
this关键字的三种用法
- 在本类的成员方法中,访问本类的成员变量(父类永远不知道子类)
- 在本类的成员方法中,访问本类的另一个成员方法。
- 在本类的构造方法中,访问本类的另一个构造方法。
//第三种的例子
public Zi(){
this(123);
}
public Zi(int n){
System.out.println(n);
}
这一种和super
一样,必须要是第一个语句且唯一,因此不能和super
同时使用
内存图:
JAVA继承的三个特点
JAVA语言是单继承的
- 一个类的直接父类只能有唯一一个
JAVA语言可以进行多级继承
- C可以继承B,B可以继承A
- 一个子类的直接父类是唯一的,但是一个父类可以拥有很多个子类
21 JAVA抽象
如果父类当中的方法不确定如何进行{}方法体的实现,那么这就是一个抽象方法。
例如:
当父类是一个形状,定义一个抽象方法则为计算这个形状的面积
此时子类可以为三角形、圆形、长方形等等
核心:abstract
关键字
抽象方法:加上abstract
关键字,然后去掉大括号,直接分号结束
抽象类:抽象方法所在的类,必须是抽象类才行。在class
之前加上abstract
即可
如何使用抽象类和抽象方法
- 不能直接创建
new
抽象对象 - 必须用一个子类来继承抽象父类
子类必须覆盖重写抽象父类当中全部的抽象方法
- 实现:去掉抽象方法的
abstract
关键字,然后补上方法体大括号
- 实现:去掉抽象方法的
- 创建子类对象来使用抽象方法
22 JAVA接口
接口就是多个类的公共规范
接口是一种引用数据类型,最重要的内容就是其中的:抽象方法
如何定义一个接口的格式:
public interface 接口名称{
// 接口内容
}
备注:换成了关键字interface
之后,编译生成的字节码文件仍然是 .java -> .class
接口中可以包含的内容有:
- 常亮
- 抽象方法
- 默认方法
- 静态方法
- 私有方法
抽象方法
定义抽象方法
注意事项:
- 接口当中的抽象方法,修饰符必须是两个固定的关键字:
public abstract
- 这两个关键字修饰符,可以选择性的省略
//格式: public abstract 返回值类型 方法名称(参数列表):
public interface MyInterfaceAbstract{
public abstract void methodAbs1();
abstract void methodAbs2();
public void methodAbs3();
void methodAbs4();
}
抽象方法的使用
使用步骤:
接口不能直接使用,必须有一个实现类来实现该接口
public class 实现类名称 implements 接口名称{}
接口的实现必须覆盖重写(实现)接口中的所有抽象方法。
- 实现:去掉
abstract
关键字,加上方法体大括号
- 实现:去掉
- 创建实现类的对象,进行使用
//已经写好了MyInterfaceAbstract接口和MyInterfaceAbstractImpl实现类
public class DemoInterface{
public static void main(String[] args){
MyInterfaceAbstractImpl imp1 = new MyInterfaceAbstractImpl();
imp1.methodAbs1();
imp1.methodAbs2();
//不能直接写
//MyInterfaceAbstract inter = new MyInterfaceAbstract();
//必须通过实现类来new
}
}
注意事项:
如果实现类并没有覆盖重写接口中的所有抽象方法,那么这个实现类自己就必须是抽象类。
默认方法
从 Java8开始,接口里面允许定义默认方法
格式:
public default 返回值类型 方法名称(参数列表){
方法体
}
接口当中的默认方法,可以解决接口升级的问题。
关于接口的默认方法:
- 接口的默认方法,可以通过接口实现类直接调用
- 接口的默认方法,也可以通过接口实现类覆盖重写
Lambda表达式和函数式编程会用到默认方法
静态方法
JAVA8开始后接口中允许定义静态方法,其格式:
public static 返回值类型 方法名称(参数列表){
方法体;
}
也就是将abstract
或者default
换成static
即可,带上方法体
注意:不能通过接口实现类的对象来调用接口当中的静态方法。
要通过接口名称,直接调用其中的静态方法。
接口名称.静态方法名(参数);
私有方法
当我们有一个公共方法,想让他能够被默认方法来使用而不想被实现类来使用的话,那么就应该把这个方法私有化。
从JAVA9开始接口当中允许定义私有方法,包括普通私有方法和静态私有方法。
- 普通私有方法:解决多个默认方法之间重复代码的问题
- 静态私有方法:解决多个静态方法之间重复代码的问题
private 返回值类型 方法名称(参数列表){
方法体;
}
private static 返回值类型 方法名称(参数列表){
方法体;
}
接口常量
接口当中也可以定义“成员变量”,但是必须使用public static final
三个关键字进行修饰
从效果上看,这其实就是接口的【常量】
一旦使用final
关键字进行修饰的话,代表的意思就是不可改变
- 接口当中的常量,是可以省略
public static final
的,但是省略了也不会改变其性质 - 接口当中的常量,必须进行赋值,不能不赋值
- 接口中常量的名称,使用完全大写的字母,用下划线进行分割。
格式:
public static final 数据类型 常量名称 = 数据值;
接口常量的访问方法:
接口名称.接口常量
接口一些注意事项
- 接口不能有静态代码块或者构造方法
一个雷的直接父类是唯一的,但是一个类可以同时实现多个接口。(要覆盖重写所有抽象方法)
- 如果两个接口的抽象方法重名的话那就只用重写一个就行了(不是说废话吗)
- 当然如果实现类是一个抽象类的话那当然不用覆盖重写所有了
- 如果实现类所实现的多个接口当中,存在重名的默认方法,那么实现类一定要对冲突的方法进行覆盖重写
- 一个类如果直接父类当中的方法和接口当中的默认方法产生了冲突,则会优先用父类的优先方法(继承优先于接口实现)
接口的多继承
- 类与类之间是单继承的,直接父类有且只有一个
- 类与接口之间是多实现的,一个类可以实现多个接口。
- 接口与接口之间是多继承的。
public interface MyInterface extends MyInterfaceA, MyInterfaceB{
public abstract void method();
}
注意事项:
- 多个父接口当中的抽象方法如果重复,问题不大,反正要重写
- 多个父接口当中的默认方法如果重复了那就有关系,必须要在子接口里面进行默认方法的覆盖重写,【而且要带着
default
关键字】
23 JAVA多态性
extends
继承和implements
实现是多态性的前提
代码当中体现多态性其实就是一句话:父类引用指向子类对象。
格式:
父类名称 对象名 = new 子类名称();
or
接口名称 对象名 = new 实现类名称();
//已经写好了父类Fu和其方法method,methodFu 以及 子类Zi和其方法method
public class DemoMulti{
public static void main(String[] args){
Fu obj = new Zi(); //左父右子
obj.method(); //调用的是子类方法
obj.methodFu(); //也能调用父类方法
}
}
多态中成员变量的使用特点
直接通过对象名称访问成员变量(
对象名称.成员变量
)- 看等号左边是谁,优先用谁,没有则向上找
间接通过成员方法访问
- 看该方法属于谁,优先用谁,没有则向上找
和之前学的成员变量访问规则没有任何变化
多态种成员方法的使用特点
口诀:编译看左边,运行看右边
对比成员变量:编译看左边,运行还看左边
使用多态的好处
对于上面这种情况
如果不用多台,只用子类,则写法是:
Teacher one = new Teacher();
one.work(); //讲课
Assistant two = new Assistant();
two.work(); //辅导
如果我们只想调用work方法而不去关心其他的,则使用多态来重写就变成
Employee one = new Teacher();
one.work(); //讲课
Employee two = new Assistant();
two.work(); //辅导
好处:无论右边new
的时候换成哪个子类对象,等号左边的调用方法都不会变化。
24 JAVA对象转型
对象的转型
向上转型
对象的向上转型,其实就是多态写法:
父类名称 对象名 = new 子类名称();
Animal animal1 = new Cat();
含义:右侧创建一个子类对象,把它当做父类来看待使用
注意事项:向上转型一定是安全的,因为是小范围转换为大范围
例如:double num = 100;
int --> double,自动类型转换
向下转型
向上转型一定是安全的,没有问题的,正确的,但是也有一个弊端
对象你向上转型为父类,那么就无法调用子类原本特有的内容。
解决方案:用对象的向下转型还原,格式为:
子类名称 对象名 = (子类名称) 父类对象
将父类对象还原成为本来的子类对象,所以必须是多态才能向下转型
例如 int num = int 10.0
类似于强制类型转换的过程,可能会出现ClassCastException异常
instanceof关键字
如何才能知道一个父类引用的对象,本来是什么子类?就用这个
格式: 对象 instanceof 类名称
返回一个boolean
结果,判断前面的对象能不能当做后面类型的实例
public static void main(String[] args){
Animal animal = new Dog();
animal.eat();
giveMeAPet(new Dog());
}
public static void giveMeAPet(Animal animal){
if(animal instanceof Dog){
Dog dog = (Dog) animal;
dog.watchHouse();
}
if(animal instanceof Cat){
Cat cat = (Cat) animal;
cat.catchMouse();
}
}
因此向下转型要做一个instanceof
判断,不然会发生类转换异常(ClassCastException
)
25 final关键字
final
代表最终、不可改变的。
常见的四种用法:
- 可以用来修饰一个类
- 可以用来修饰一个方法
- 还可以用来修饰一个局部变量
- 还可以用来修饰一个成员变量
final关键字用于修饰类
public final class 类名称{
...
}
含义:当前这个类不能有任何的子类
加上final
后,就不能用其他的类来继承这个类了
因此一个类如果是final
类,那么其中所有的成员方法都无法进行覆盖重写了。
final关键字用于修饰成员方法
当final
关键字用来修饰一个方法的时候,这个方法就是最终方法,不能进行覆盖重写
格式:
修饰符 final 返回值类型 方法名称(参数列表) {
方法体
}
所以abstract
关键字和final
关键字是不能同时在一个类、方法上使用的,因为一个必须覆盖重写,另一个又不能覆盖重写,冲突了。
修饰局部变量
一旦局部变量被final
了,那就只能被赋值一次,赋值后不能更改
int num1 = 100;
num1 = 200; //ok
final int num2 = 100;
num2 = 200; //错误写法
final int num3;
num3 = 100; //ok
对于基本数据类型来说,不可变说的是变量当中的数据不可以改变
对于引用数据类型来说,不可变说的是变量当中的地址不可改变
修饰成员变量
对于成员变量来说,如果使用final
关键字修饰,那么这个变量也照样是不可变
注意事项:
- 由于成员变量具有默认值,所以用了
final
之后必须手动赋值,不会再也默认值了 - 对于
final
的成员变量,要么使用直接赋值,要么通过构造方法赋值,二选一 - 必须保证类当中的所有重载的构造方法,都会对
final
的成员变量进行赋值
26 JAVA权限修饰符
Java中有四种权限修饰符:
public > protected > (留白不写) > private
同类 Y Y Y Y
同包 Y Y Y N
不同包子类 Y Y N N
不同包和类 Y N N N
同一个类:我自己,四个都可以
同一个包:我邻居,除了自己private
的都可以
不同包但子类:我儿子,默认的就不行了
不同包非子类:陌生人,只有public
才可以
定义一个类的时候,权限修饰符的规则是:
1.外部类: public / (不写)
2.成员内部类:public / protected / (不写) / private
3.局部内部类:只能什么都不能写
小tips:如果局部内部类想访问所在方法的局部变量,那么这个局部变量必须是有效final
的
原因:
new
出来的对象在堆内存中,局部变量是跟着方法走的,在栈内存中。方法运行结束之后,立刻出栈,局部变量就会立刻消失。但是new
出来的对象会在堆当中持续存在,直到垃圾回收消失。
27 JAVA内部类
如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类
分类:1.成员内部类 2.局部内部类(包含匿名内部类)
1.成员内部类
成员内部类的定义格式:
修饰符 class 外部类名称{
修饰符 class 内部类名称{
//...
}
//...
}
内部类使用外部类访问不受限制
外部类用内部类则需要内部类对象
public class Body{
public class Heart{
public void beat(){
System.out.println("内部类的方法");
System.out.println(name); //可以访问
}
}
private String name;
public void methodBody(){
System.out.println("外部类的方法");
Heart heart = new Heart();
heart.beat();
}
}
成员内部类的使用
有两种方式
- 间接方式:在外部类的方法当中,使用内部类;然后
main
只是调用外部类的方法
Body body = new Body(); //外部类的对象
//通过外部类的对象,调用外部类的方法,里面间接再使用内部类Heart
body.methodBody();
- 直接方式
类名称 对象名 = new 类名称();
外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称()
Body.Heart heart = new Body().new Heart();
heart.beat();
成员内部类的重名处理
public class Outer{
int num = 10; //外部类的成员变量
public class Inner{
int num = 20; //内部类的成员变量
public void methodInner(){
int num = 30; //内部类的方法的局部变量
sout(num); //局部变量
sout(this.num); //内部成员变量
sout(Outer.this.num); //外部类的成员变量
}
}
}
格式就是:外部类名称.this.外部类成员变量名
2.局部内部类
如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。
局部的意义:只有当前所属的方法才能使用它,出了这个方法外面就不能使用了。
定义格式:
修饰符 class 外部类名称{
修饰符 返回值类型 外部类方法名称(参数){
class 局部内部类名称{
...
}
}
}
public class Outer{
public void methodOuter(){
class Inner{ //局部内部类
int num = 10;
public void methodInner(){
System.out.println(num);
}
}
Inner inner = new Inner();
inner.methodInner(); //只有methodOuter方法才能使用Inner类
//因此外面要是想使用Inner类的方法,就要通过methodOuter来间接使用
}
}
匿名内部类
如果父类的实现类或者父类的子类,只需要使用唯一的一次的话
那么这种情况下就可以省略该类的定义,而改为使用匿名内部类
匿名内部类的定义格式:
接口名称 对象名 = new 接口名称() {
//覆盖重写所有抽象方法
};
public class DemoMain{
public static void main(String[] args){
MyInterface obj = new Myinterface(){
@Override //覆盖重写接口所有方法
public void method(){
System.out.println("匿名内部类实现");
}
};
}
}
如果不想写实现类的话,直接new
接口,然后覆盖重写方法,就可以省事了
注意事项
匿名内部类在创建对象的时候,只能使用唯一一次。
- 想用多次的话,要么重写两次,要么直接去建一个实现类吧
匿名对象在调用方法的时候,只能调用唯一一次。
- 想多次调用同一个对象的方法的话,就取个名字来接收吧
- 匿名内部类是省略了实现类/子类名称,匿名对象是省略了对象名称