Java多态的理解--父类引用指向子类的对象
1、定义:在程序中定义的某个引用变量具体所指向的类型和调用的方法在编码时并不能确定,而只有在运行的时候才能确定。
2、简单理解:你定义的引用变量是父类型,而你创建的对象是子类型,用这个父类型的引用变量指向这个子类型的对象,然后当你调用这个对象的方法的时候,具体是调用的父类的还是子类的方法只有在运行的时候才能确定,可能调用的是父类的方法也有可能调用的子类的方法,这样不用修改代码,就可以让成徐选择多个运行的状态,这就是多态。
3、要点:
1)多态的三大必要条件:继承、覆写、父类引用指向子类对象。
2)父类的实例方法可以被覆盖,属性不能被覆盖。
3)父类的引用可以访问父类的所有属性和方法,可以访问子类覆盖的方法(引用指向子类对象的话),但不能访问子类的属性(属性不能被覆盖),及子类新添加的方法。
4)父类引用会调用子类覆盖的方法即动态绑定,指定是程序在执行时(非编译期间)会判断引用对象的实际类型,并调用其相应的方法。
4、关于在集合中使用泛型及多态的问题
先看下以下代码能否通过编译?
实例一:
package com.linwei; import java.util.ArrayList; import java.util.List; abstract class Animal { public void eat(){ System.out.println("animal eating"); } } class Dog extends Animal{ public void bark(){ System.out.println("dog barking"); } } class Cat extends Animal{ public void meow(){ System.out.println("cat meowing"); } } public class TestDuotai { public void showAnimals(List<Animal> animals ){ //animals.add(new Cat()); for(Animal a : animals){ a.eat(); } } public static void main(String[] args) { ArrayList<Dog> dogs=new ArrayList<Dog>(); dogs.add(new Dog()); dogs.add(new Dog()); dogs.add(new Dog()); new TestDuotai().showAnimals(dogs); } }
答:不能编译通过。
问题出在该方法:showAnimals(List<Animal> animals )。该方法接受的是Animal类型的集合,按照多态的概念来说,Dog类是Animal类的子类,传入Dog类型的集合应该是没问题的,但为什么不能编译通过呢?
原因(反证法验证):
1)假设能够编译通过,则说明在一个需要List<Animal> 的地方可以传入ArrayList<Dog>对象,不过如果继续往方法的内部思考,就会存在问题。
2)见showAnimals(List<Animal> animals )方法内部的注释行://animals.add(new Cat()),假设把注释去掉,则由于Cat类也是Animal类的子类,所以该行代码也是完全没有问题的。
3)但回到方法的调用处:new TestDuotai().showAnimals(dogs),我们就会发现实际上我们传入showAnimals(List<Animal> animals )方法的是Dog类型的集合,但如果在方法中执行到animals.add(new Cat())代码时,意思是要往ArrayList<Dog>中传入一个Cat类型的对象,如此一来就涉及到了泛型安全性的问题,此时就不能通过编译器的编译(集合泛型类型的检查发生在编译期,而数组类型的检查则在运行期)。
4)结论:你不能向一个参数类型为ArrayList<Animal>的方法中传递ArrayList<Dog>类型的对象。
5) 关于以上第4)点的补充实例,代码如下所示。
package com.linwei; import java.util.ArrayList; import java.util.List; abstract class Animal { public void eat(){ System.out.println("animal eating"); } } class Dog extends Animal{ public void bark(){ System.out.println("dog barking"); } } class Cat extends Animal{ public void meow(){ System.out.println("cat meowing"); } } public class TestDuotai { public void showAnimals(List<Animal> animals ){ for(Animal a : animals){ a.eat(); } } public static void main(String[] args) { ArrayList<Dog> dogs1=new ArrayList<Animal>(); //不能编译通过 ArrayList<Animal> animals1=new ArrayList<Dog>(); //不能编译通过 List<Animal> list=new ArrayList<Animal>(); ArrayList<Dog> dogs=new ArrayList<Dog>(); ArrayList<Animal> animals = dogs; //不能编译通过 List<Dog> dogList=dogs; ArrayList<Object> objects=new ArrayList<Object>(); List<Object> objectList=objects; ArrayList<Object> objs=new ArrayList<Dog>(); //不能编译通过 } }
上一篇: Java的垃圾收集机制
推荐阅读
-
java基础 静态 static 问在多态中,子类静态方法覆盖父类静态方法时,父类引用调用的是哪个方法?
-
Java多态的理解--父类引用指向子类的对象
-
对继承与多态、成员变量的隐藏和方法重写、super关键字、final关键字、对象的上转型对象、抽象类的学习理解(java)
-
java基础 静态 static 问在多态中,子类静态方法覆盖父类静态方法时,父类引用调用的是哪个方法?
-
JAVA 面向对象(继承,向上/下转型,子类调用父类的构造函数)
-
Java在创建子类对象的同时会不会创建父类对象 java创建子类对象父类对象
-
子类继承父类中的static模块、构造方法执行顺序及java多态性
-
强转父类后对象依然是子类的引用