目录
一、什么是多态?
多态的本质 : 引用数据类型 子父类/实现类和父接口 之间的类型转换问题;
因为基本数据类型有 "类型转换" 这个特性;
前提 : 基本数据类型之间有大小关系 [double > float > long > int > byte,short,char]
自动提升 : 大类型的变量接收小类型的值/变量
强制转换 : 小类型的变量强制接收大类型的值/变量 [有弊端 : 不推荐使用]
Java中引用数据类型的种类繁多,也想有想Java基本数据类型 '类型转换' 这样的特性
前提 : 必须有继承关系/实现关系 -> 必要条件
向上转型 : 父引用接收子类对象 : Fu fu = new Zi();
向下转型 : 子引用"强制"接收父引用 : Zi zi = (Zi) fu; [有风险 : 把父引用转换成了不同类型的子类对象]public class Demo { public static void main(String[] args) { //向上转型 : 父类引用直接接收子类对象 Animal animal = new Dog(); //任意一条狗都是一只动物 //向下转型 : 子引用强制接收父引用 [前提 : 这个父引用确实接收的是一只狗对象] Dog dog = (Dog)animal; //报错 : 类型转换异常 //Cat cat = (Cat)animal; //向上转型 : 父类引用直接接收子类对象 Fu fu = new Zi(); //向下转型 : 子引用强制接收父引用 Zi zi = (Zi) fu; } } class Animal{ } class Dog extends Animal{ } class Cat extends Animal{ } interface Fu{ } class Zi implements Fu{ }
二、多态前提下成员的访问特点
多态前提下成员变量的访问特点 : 就近原则
多态前提下构造方法的访问特点 : 子类要加载优先加载父类
多态前提下成员方法的访问特点 : 动态绑定
当父引用启动方法时,具体执行的是哪个方法要看父引用具体接收的是哪个类型的对象;注意!父引用无法访问子类特有的成员 ! [因为子类对象的功能才是最多的]
三、多态使用场景
1. 把父引用作为方法的形参,那么传入方法实参的对象类型有 n + 1 种;
// 举例 :
public void show(Object obj){//传入任意类型的实参对象
}
2. 把父类型作为容器的数据类型,那么容器内可以存储的元素类型有 n + 1 种; //举例 : Object[] , ArrayList<Object>
3. 把父类型作为方法的返回值类型 , 那么方法内返回结果的对象类型有 n + 1 种;
//举例 :
public Object get(){
return 任意类型的结果;
}
4. 异常体系中 : 抛出父类型异常,子类型异常也抛出; / 捕获父类型异常,子类型异常也一并捕获了;/* 动态绑定 : 父引用调用方法,动态的绑定等号右边真实对象的类型 多态的弊端 : 父引用不能访问子类的特有成员 */ public class Demo { public static void main(String[] args) { //创建多态对象 [这么用的很少] Animal a1 = new Dog(); Animal a2 = new Cat(); //对象调用方法 : 看对象的真实类型[等号右边具体的类型] a1.eat(); //a1.lookHome(); a2.eat(); System.out.println("--------------------------"); //动态绑定真实的使用场景 -> 多态的真实使用场景 Dog dog = new Dog(); Cat cat = new Cat(); useAnimal(dog); useAnimal(cat); } /* 参数传递时 有多态: 1. Animal animal = new Dog(); 2. Animal animal = new Cat(); */ public static void useAnimal(Animal animal){ animal.eat(); } } class Animal{ public void eat(){ System.out.println("动物吃东西"); } } class Dog extends Animal{ @Override public void eat() { System.out.println("狗吃骨头"); } public void lookHome(){ } } class Cat extends Animal{ @Override public void eat() { System.out.println("猫吃罐头"); } }
四、多态弊端的解决
多态弊端的解决 : 向下转型
向下转型 : 子引用"强制"接收父引用 : Zi zi = (Zi) fu; [有风险 : 把父引用转换成了不同类型的子类对象]
如何判断一个引用是哪个类型的对象 :
1. instanceof -> 格式 : 引用 instanceof 类型 -> 结果 : boolean 类型
2. 通过字节码对象判断类型 -> 原因 : 因为一个类的字节码对象只有一个! [通过不同方式获取类的字节码对象如果是同一个对象,那么说明类型是一致的!]
获取类字节码对象的方式 :
a. 对象.getClass() : 获取此对象所属类的字节码对象
b. 类名.class : 获取此类的字节码对象
if(对象名.getClass() == 类名.class){
//能进来说明 前面的对象是后面那个类型的
}/* 如何解决多态的弊端 : 向下转型 */ public class Demo { public static void main(String[] args) { // Dog dog = new Dog(); // useAnimal(dog); //匿名[没有对象名的]对象 useAnimal(new Dog()); //useAnimal(new Cat()); } public static void useAnimal(Animal animal){//= new Dog(); animal.eat(); //启动狗和猫类的特有行为 //animal.lookHome(); //多态的弊端 : 父引用无法访问子类特有成员 //向下转型有风险 : 提前做判断 -> 是狗转狗 , 是猫转猫 if (animal instanceof Dog){ Dog dog = (Dog) animal; dog.lookHome(); } if (animal instanceof Cat){//false Cat cat = (Cat) animal; cat.catchMouse(); } if (animal.getClass() == Dog.class){ Dog dog = (Dog) animal; dog.lookHome(); } if (animal.getClass() == Cat.class){ Cat cat = (Cat) animal; cat.catchMouse(); } } } class Animal{ public void eat(){ System.out.println("动物吃东西"); } } class Dog extends Animal { @Override public void eat() { System.out.println("狗吃骨头"); } public void lookHome(){ System.out.println("狗看家"); } } class Cat extends Animal { @Override public void eat() { System.out.println("猫吃罐头"); } public void catchMouse(){ System.out.println("猫抓老鼠"); } }