一、多态
1.多态的常见形式
父类类型 对象名称 = new 子类构造器;
接口 对象名称 = new 实现类构造器;
2.多态中成员访问特点
方法调用:编译看左边,运行看右边。
变量调用:编译看左边,运行看左边。
(多态侧重行为多态)
3.多态的前提
有继承/实现关系;有父类引用指向子类对象;有方法重写。
4.多态的优势
在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性和便利。
5.多态产生的一个问题:多态下不能使用子类的独有功能,如何解决?强制类型转换。
6.多态下引用数据类型的数据转换
自动类型转换(从子到父):子类对象赋值给父类类型的变量指向。
例如:Dog d = new Animal();
强制类型转换(从父到子):子类 对象变量 = (子类)父类类型的变量。
例如:Animal a = new Dog();
Dog d = (Dog) a;
如果转换后的类型和对象真实类型不是同一种类型,那么在转换的时候就会出现异常ClassCastException。
Java建议在强制类型转换前使用instanceof判断当前对象的真实类型,再进行强制转换。
if(usb instanceof KeyBoard){
KeyBoard k = (KeyBoard) usb;
} else if(usb instanceof Mouse){
Mouse m = (Mouse) usb;
}
二、内部类
1.内部类的概述
内部类就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解为(宿主)。
2.内部类的基本作用
内部类通常可以方便访问外部类的成员,包括私有的成员。
内部类提供了更好的封装性,内部类本身就可以用private、protected等修饰,封装性可以做更多控制。
3.内部类的分类
(1)静态内部类
(2)成员内部类
(3)局部内部类
(4)匿名内部类
3.静态内部类
public class Outer{
public static class Inner{
}
}
(1)有static修饰,属于外部类本身。
(2)静态内部类创建对象的格式:
外部类名.内部类名 对象名 = new 外部类名.内部类构造器;
Outer.Inner in = new Outer.Inner();
(3)静态内部类中可以直接访问外部类的静态成员。
(4)静态内部类中不可以直接访问外部类的实例成员,必须用外部类对象访问。
4.成员内部类
public class Outer{
public class Inner{
}
}
(1)无static修饰,属于外部类的对象。
(2)JDK16之前,成员内部类中不能定义静态成员,JDK16开始也可以定义静态成员了。
(3)成员内部类创建对象的格式:
外部类名.内部类名 对象名 = new 外部类构造器.new 内部类构造器();
Outer.Inner in = new Outer.new Inner();
(4)成员内部类中可以直接访问外部类的静态成员。
(5)成员内部类的实例方法中可以直接访问外部类的实例成员,因为必须现有外部类对象,才能有内部类对象,所以可以直接访问外部类对象的实例成员。
(6)在成员内部类中访问所在外部类对象,格式:外部类名.this
5.局部内部类(鸡肋)
局部内部类放在方法、代码块、构造器等执行体中。
6.匿名内部类(重点)
Animal a = new Animal{
public void run(){
System.out.println("老虎跑得快");
}
}
(1)概述:本质上是一个没有名字的局部内部类。
(2)作用:方便创建子类对象,最终目的是为了简化代码编写。
(3)格式:
new 类|抽象类名|接口名(){
重写方法;
};
(4)特点:匿名内部类是一个没有名字的内部类,同时也代表一个对象。
匿名内部类产生的对象类型,相当于当前new的那个类型的子类类型。
(5)总结:匿名内部类可以作为一个对象,直接传输给方法。
匿名内部类通常是在开发中调用别人的方法时,别人需要我们写的时候才会定义出来使用。
三、常用API
1.Object
方法名 | 说明 |
public String toString() | 默认是返回当前对象在堆内存中的地址信息:类的全限名@内存地址 |
public boolean equals(Object o) | 默认是比较当前对象与另一个对象的地址是否相同,相同返回true,不同返回false |
父类toString方法存在的意义就是为了被子类重写,以便返回对象的内容信息,而不是地址信息。
父类equals方法存在的意义就是为了被子类重写,以便子类自己来定制比较规则(比如比较对象内容)。
两个方法都可以用Alt+Insert快捷键自动生成。
写的equals重写方法:
@Override
public boolean equals(Object o) {
//1.判断0是不是学生类型
if (o instanceof Student) {
Student s2 = (Student) o;
//2.判断两个对象的内容是否一样
if (this.name.equals(s2.name)
&& this.age == s2.age
&& this.sex == s2.sex) {
return true;
} else {
return false;
}
return this.name.equals(s2.name)
&& this.age == s2.age
&& this.sex == s2.sex;
} else {
//学生只能和学生比较,否则结果一定是false
return false;
}
}
官方写的euqals重写方法
@Override
public boolean equals(Object o) {
//1.判断是否是同一个对象比较,如果是,返回true
if (this == o) return true;
//2.如果o是null返回false 如果o不是学生类型返回false
if (o == null || getClass() != o.getClass()) return false;
//3.说明o一定是学生类型且不为null
Student student = (Student) o;
return sex == student.sex && age == student.age && Objects.equals(name, student.name);
}
2.Objects
方法名 | 说明 |
public static boolean equals(Object a, Object b) | 比较两个对象的,底层会先进行非空判断,从而可以避免空指针异常。再进行equals比较 |
public static boolean isNull(Object obj) | 判断变量是否为null ,为null返回true ,反之 |
建议使用Objects的equals方法,在进行对象的比较会更安全,因为Objects的equals会避免空指针异常。
3.StringBuilder
StringBuilder是一个可变的字符串类,可以把它看成是一个对象容器。
作用:提高字符串的操作效率,如拼接、修改等。
StringBuilder构造器:
名称 | 说明 |
public StringBuilder() | 创建一个空白的可变的字符串对象,不包含任何内容 |
public StringBuilder(String str) | 创建一个指定字符串内容的可变字符串对象 |
StringBuilder常用方法
方法名称 | 说明 |
public StringBuilder append(任意类型) | 添加数据并返回StringBuilder对象本身 |
public StringBuilder reverse() | 将对象的内容反转 |
public int length() | 返回对象内容长度 |
public String toString() | 通过toString()就可以实现把StringBuilder转换为String |
注意:StringBuilder只是拼接字符串的手段:效率好,最终结果还是要恢复成String类型。
StringBuilder恢复成String类型,用toString()方法。
例如:StringBuilder sb = new StringBuilder();
sb.append("123");
String rs = sb.toString();
案例:打印整型数组内容
public class StringBuilderDemo2 {
public static void main(String[] args) {
int[] arr1 = null;
int[] arr2 = {};
int[] arr3 = {20, 30, 40};
System.out.println(toString(arr1));
System.out.println(toString(arr2));
System.out.println(toString(arr3));
}
/**
* 定义方法接收任意整形数组,返回数组内容格式
*/
public static String toString(int[] arr) {
if (arr != null) {
//开始拼接
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]).append(i == arr.length - 1 ? "" : ", ");
}
sb.append("]");
return sb.toString();
} else {
return null;
}
}
}
4.Math(不能实例化)
方法名 | 说明 |
public static int abs(int a) | 获取参数绝对值 |
public static double ceil(double a) | 向上取整 |
public static double floor(double a) | 向下取整 |
public static int round(float a) | 四舍五入 |
public static int max(int a,int b) | 获取两个int值中的较大值 |
public static double pow(double a,double b) | 返回a的b次幂 |
public static double random() | 返回值为double的随机值,范围[0.0,1.0) |
拓展:3 - 9之间的随机数 (0 - 6)+3
int data = (int) (Math.random() * 7) + 3;
System.out.println(data);
5.System(不能实例化)
方法名 | 说明 |
public static void exit(int status) | 终止当前运行的Java虚拟机,非0表示异常终止 |
public static long currentTimeMills() | 返回当前系统的时间毫秒值形式 |
public static void arraycopy(数据源数组,起始索引,目的地数组,歧视索引,拷贝个数) | 数组拷贝 |
时间毫秒值:指的是从1970年1月1日 00:00:00走到此刻的总的毫秒值
currentTimeMills()方法常用于进行性能分析
6.BigDecimal:用于解决浮点型运算精度失真的问题
方法名 | 说明 |
public BigDecimal add(BigDecimal b) | 加法 |
public BigDecimal substract(BigDecimal b) | 减法 |
public BigDecimal multiply(BigDecimal b) | 乘法 |
public BigDecimal divide(BigDecimal b) | 除法 |
public BigDecimal add(另一个BigDecimal对象,精确几位,舍入模式) | 除法 |
禁止使用构造方法BigDecimal(double)的方法把double值转化为BigDecimal对象,此方法存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。
优先推荐入参为String的构造方法,或使用BigDecimal的valueOf方法,此方法内部其实执行了Double的toString,而Double的toString按double的实际能表达的精度对尾数进行了截断。
BigDecimal recommend1 = new BigDecimal("0.1");
BigDecimal recommend2 = BigDecimal.valueOf(0.1);//优先使用此方法