多态
多态:同一个对象具有多种形态】
作用:把不同的数据类型进行统一;给予程序极高的可扩展性
多态意味着父类型的变量可以引用子类型的对象,向上转型,但本质不变
(子类有对应方法则执行子类方法,没有则执行父类方法——>继承的影响)
动态编译
分类:具体类多态,抽象类多态,接口多态
前提:
-
有继承或者实现关系
-
有方法重写
-
有父类的引用指向子类对象:
- 可以将一个子类的对象赋值给父类(向上转型)
- 但是不可以将父类的对象赋值给子类
成员的访问特点
Person s2 = new Student();
1.特点总结:
成员变量:编译看左边,执行看左边
成员方法:编译看左边(父类),执行看右边(子类)
不能执行只有子类中有,而父类中没有的方法,编译不会通过
可以执行只有父类中有,而子类中没有的方法(子类继承父类的方法)
2.原因:
-
成员方法有重写,成员变量没有
3.代码实验
public class Animal { public int age = 40; public void eat(){ System.out.println("动物吃东西"); } }
public class Cat extends Animal{ public int age = 20; public int weight =10; @Override public void eat() { System.out.println("猫吃鱼"); } public void playGame(){ System.out.println("猫玩捉迷藏"); } }
public class demo { public static void main(String[] args) { Animal a = new Cat(); System.out.println(a.age); // 40,调用的是父类中的成员变量 System.out.println(a.weight); // 编译不通过,父类中没有该变量 a.eat(); //猫吃鱼,调用的是子类的中的成员方法 ??a.playGame(); //编译不通过,父类中没有该方法 } }
4.好处与弊端
优点:
-
具体体现:当定义方法的时候,使用父类作为参数,将来使用的时候,使用具体的(可能重写)子类进行操作
弊端:
-
不能使用子类的特有功能
应用场景一
父类
public class Animal { public void eat(){ System.out.println("动物吃东西"); } }
子类
public class Cat extends Animal{ @Override public void eat() { System.out.println("猫吃鱼"); } }
public class Dog extends Animal{ public void eat(){ System.out.println("狗吃骨头"); } }
动物操作类
public class AnimalOperator { // public void useAnimal(Cat c ){c.eat();} // public void useAnimal(Dog d){d.eat();} public void useAnimal(Animal a){ a.eat(); } }
执行
public class demo { public static void main(String[] args) { AnimalOperator animalOperator = new AnimalOperator(); Cat c = new Cat(); animalOperator.useAnimal(c); Dog d = new Dog(); animalOperator.useAnimal(d); } }
当调用animalOperator.useAnimal(c);时,将 Cat c 传入操作类的方法参数中:
由于猫继承了动物,猫也是动物:
等价于
Aniaml a = new Cat();
即多态的形式,调用方法时,编译看左,执行看右,调用的是子类的方法
具体体现:当定义方法的时候,使用父类作为参数,将来使用的时候,使用具体的(可能重写)子类进行操作
作用:当加入更多类的动物(子类)时,不用一次次修改AnimalOperator类中的方法参数,直接传入父类的引用
-
极高的可扩展性
应用场景二
经理类继承雇员类(经理也是雇员)
当需要雇员列表时,
-
需要全部雇员,包括经理
-
列表中全部元素为同一个类型
多态中的转型
例子
public class demo { public static void main(String[] args) { //向上转型 Animal a = new Cat(); Animal a1 = new Animal(); a.eat(); // a.playGame(); //向下转型 Cat c = (Cat) a; //强制转型,将一个父类的对象强制转化成子类,可以使用子类的方法 Cat c1 = (Cat) a1; //报错,ClassCastException,因为a1本来就不是猫类 c.eat(); c.palyGame(); System.out.println(a); //com.oop.demo07duotai.Cat@1b6d3586 System.out.println(c); //com.oop.demo07duotai.Cat@1b6d3586 //说明ac是同一个对象
public class Cat extends Animal{ @Override public void eat() { System.out.println("猫吃鱼"); } public void palyGame(){ System.out.println("猫捉迷藏"); } }
public class Animal { public void eat(){ System.out.println("动物吃东西"); } }
注意
-
只能在继承层次里进行类型转换
-
善于使用instanceof进行检查
如,可以使用 if (a instanceof cat){ Cat c = (Cat) a; }
总结
-
将一个子类的引用赋给父类,编译器是允许的(Animal a = new Cat(); )
-
但是将一个父类的引用赋给子类变量是不允许的,必须进行类型转换(但是不能随意转,要判断检查)
类型转换的唯一原因:
-
在暂时忽略了对象的实际类型后(多态性的好处,向上转型便于扩展),现在需要对象的原本的全部功能
-
现在需要将对象转换为他该是的类型(向下转型,解决多态弊端),以使用子类中特有的方法
内存分析
黑马程序员全套Java教程Java基础入门视频教程,零基础小白自学Java必备教程哔哩哔哩_bilibili
P173