1、多态
概述
-
面向对象的三大特征:封装、继承、多态
-
多态的形式:
父类 对象名称 = new 子类构造器; 接口 对象名称 = new 实现类构造器; 父类类型范围 > 子类类型范围
-
相同类型的变量调用同一个行为,在不同状态下可以表现出不同的特征
-
使用前提:
- 必须存在继承或实现关系
- 存在父类类型的变量引用子类类型的对象
- 存在方法重写
-
识别技巧:
- 对于方法调用:编译看左,运行看右
- 对于变量的调用:编译看左,运行也看左
public class PolymorphicDemo { public static void main(String[] args) { //多态:父类 对象名称 = new 子类构造器; Animal dog = new Dog(); Animal rabbit = new Rabbit(); //调用方法 dog.run(); //狗类run方法 rabbit.run(); //兔类run方法 //调用变量 System.out.println(dog.name); //动物类 System.out.println(rabbit.name); //动物类 } } class Animal{ public String name = "动物类"; public void run(){ System.out.println("动物类run方法"); } } class Dog extends Animal{ public String name = "狗类"; @Override public void run() { System.out.println("狗类run方法"); } } class Rabbit extends Animal{ public String name = "兔类"; @Override public void run() { System.out.println("兔类run方法"); } }
多态的优劣势
优势:
- 在多态形式下,右边对象可以实现组件化解耦,右边的对象可以随意的切换 后续业务功能代码可以不用修改,而功能可以产生变化便于扩展和维护
- 父类类型作为方法形参,传递子类对象给方法,进行不同的调用,体现出多态的扩展性
劣势:
- 多态形式下编译看左边,所以无法调用子类独有功能
多态的类型转换
引用类型自动类型转换
- 子类对象或者子类类型的变量可以直接自动类型转换给父类类型的变量
- 并没有解决多态的劣势,无法调用子类的特有功能
引用类型强制类型转换
-
父类对象或者父类类型的变量赋值给子类类型的变量必须进行强制类型转换
-
格式:
子类名称 对象名称 = (子类名称)父类对象或者父类类型的变量
-
强制转换异常问题:原因是转换前后对象类型不一致
类型转换异常:ClassCastException
-
建议进行引用类型强制转换之前,先判断对象的真实类型
对象类型 instanceof 类型 //判断前面对象变量的真实类型是否是后面的类型或者其子类类型
public class PolymorphicDemo01 { public static void main(String[] args) { Animal animal = new Dog(); animal.run(); //狗子快跑 //animal.lookDoor(); // 多态下不能直接调用子类独有功能——多态的劣势 //先不进行判断真是类型直接强转(在知道真实类型的情况下) Dog dog = (Dog)animal; // 强制类型转换成Dog类 dog.lookDoor(); //狗子看门 // 若不知道真实类型则可能出现类型转换异常 Animal animal2 = new Cat(); // Dog dog2 = (Dog) animal2; // 猫类型转成狗类型所以出错 // 在进行引用类型强制转换之前,先用instanceof判断对象的真实类型: if(animal2 instanceof Dog){ // animal2真实类型真的是狗 Dog dog2 = (Dog) animal2; dog2.lookDoor(); //狗子看门 }else if(animal2 instanceof Cat){ // animal2真实类型是猫 Cat cat = (Cat) animal2; cat.catchMouse(); } } } //父类 class Animal{ public void run(){ System.out.println("动物都能跑"); } } //子类猫 class Cat extends Animal { @Override public void run(){ System.out.println("猫猫快跑"); } // 独有功能 public void catchMouse(){ System.out.println("猫抓老鼠"); } } //子类狗 class Dog extends Animal { @Override public void run(){ System.out.println("狗子快跑"); } // 独有功能 public void lookDoor(){ System.out.println("狗子看门"); } }
2、内部类
概述
- 定义在类里面的类就是内部类
- 内部类可以提供更好的封装性,内部类有更多的权限修饰符,封装性有更多的控制
- 内部类的分类:
- 静态内部类
- 实例内部类
- 局部内部类
- 匿名内部类
静态内部类
-
用static修饰的内部类,属于外部类本身所以会和外部类一起加载一次,只有一份
-
类有的成分静态内部类都具备,只是位置在一个类中,与普通类基本没区别
-
访问格式:
外部类名称.内部类名称
-
创建对象的格式:
外部类名称.内部类名称 对象名称 = new 外部类名称.内部类名称
-
静态内部类是否可以直接访问外部类的静态成员? 可以的,外部类的静态成员只有一份是被共享的
-
静态内部类是否可以直接访问外部类的实例成员? 不可以的,外部类的实例成员必须用外部类的对象访问
实例内部类
-
无static修饰的内部类,属于外部类的每个对象,与对象一起加载
-
不能定义静态成员,可以定义常量
-
实例内部类中访问所在外部类对象的格式:
外部类名称.this
-
创建对象的格式:
外部类名称.内部类名称 对象名称 = new 外部类构造器.new 内部类构造器;
public class InnerClass { public static void main(String[] args) { // 外部类名称.内部类名称 对象名称 = new 外部类构造器.new 内部类构造器; Outter.Inner inner = new Outter().new Inner(); inner.jump(); } } class Outter{ private int weight = 30; // 实例内部类:属于外部类对象。 public class Inner { private int weight = 20; //可以定义常量 public static final String SCHOOL_NAME = "北大" ; public void jump() { int weight = 10; System.out.println(weight); // 10 System.out.println(this.weight); // 20 System.out.println(Outter.this.weight); // 30 } } }
-
实例内部类是否可以直接访问外部类的静态成员?可以的,外部类的静态成员可以被共享,只有一份
-
实例内部类是否可以直接访问外部类的实例成员?可以的,实例内部类和外部类的实例成员都在外部类对象中
局部内部类
- 定义在方法中、构造器中、代码块中、for循环中的内部类
- 只能定义实例成员,不能定义静态成员
- 可以定义常量
匿名内部类
-
格式:
new 类名|接口|抽象类(形参){ 被重写的方法 }
-
特点:
- 是没有名字的内部类
- 会自动创建当前匿名内部类的一个对象返回
- 匿名内部类返回的对象类型相当于是继承了当前所new的那个类型的子类
public class AnonymityDemo01 {
public static void main(String[] args) {
// Animal animal = new Cat();
// animal.run();
//匿名内部类
Animal animal = new Animal() {
@Override
public void run() {
System.out.println("猫跑得挺快");
}
};
animal.run();
}
}
//正常子类继承父类后重写方法,创建对象调用方法(繁琐)
//class Cat extends Animal{
// @Override
// public void run() {
// System.out.println("猫跑得挺快");
// }
//}
abstract class Animal{
public abstract void run();
}
3、权限修饰符
-
权限由小到大
private → 缺省 → protected → public
-
可以修饰成员变量,方法,构造器;不同修饰符修饰的成员能够被访问的权限也将受到限制
-
四种修饰符的访问权限范围: private 缺省 protected public 本类中 √ √ √ √ 本包下其他类中 X √ √ √ 其他包下的类中 X X X √ 其他包下的子类中 X X √ √
4、Object类常用方法
- Object类是Java中一切类的祖宗类,一个类要么默认继承了Object类,要么间接继承了Object类
- Object类中的方法是一切类都可以使用的
①public String toString();
- 默认是返回当前对象在堆内存中的地址信息
- 直接输出对象可以省略toString()不写,默认调用
- 直接输出对象调用toString()方法返回对象的地址其实是没有意义的,所以toString()方法存在的意义是为了被子类重写,以便返回对象的数据内容输出
②public boolean equals(Object obj)
- 默认是判断两个对象的堆内存地址是否一样,判断是否是同一个对象,这一点可以直接用==代替equals
- 存在的意义是为了让子类重写,便于自定义比较规则
5、Objects类
-
jdk1.7才开始有
-
Objects类依然是Object的子孙类
-
public static boolean equals(Object a, Object b)
判断两个对象是否相等,可以避免空指针异常
-
public static boolean isNull(Object obj)
判断对象是否为null,如果为null就返回true