用一句话概括就是:事物在运行过程中存在不同的状态。多态的存在有三个前提:
1.要有继承关系
2.子类要重写父类的方法
3.父类引用指向子类对,
但是其中又有很多细节需要注意。首先我们定义两个类,一个父类Animal,一个子类Cat。
以上的三段代码充分体现了多态的三个前提,即:
1、存在继承关系
Cat类继承了Animal类
2、子类要重写父类的方法
子类重写(override)了父类的两个成员方法eat(),sleep()。其中eat()是非静态的,sleep()是静态的(static)。
3、父类数据类型的引用指向子类对象
测试类Demo_Test1中 Animal am = new Cat();语句在堆内存中开辟了子类(Cat)的对象,并把栈内存中的父类(Animal)的引用指向了这个Cat对象。
到此,满足了Java多态的的必要三个前提。
如果再深究一点呢,我们可以看看上面测试类的输出结果,或许对多态会有更深层次的认识。猜一猜上面的结果是什么。
那么我们可以根据以上情况总结出多态成员访问的特点:
成员变量
编译看左边(父类),运行看左边(父类)
成员方法
编译看左边(父类),运行看右边(子类)。动态绑定
静态方法
编译看左边(父类),运行看左边(父类)。
(静态和类相关,算不上重写,所以,访问还是左边的)
只有非静态的成员方法,编译看左边,运行看右边
那么多态有什么弊端呢?有的,即多态后不能使用子类特有的属性和方法。往上面的代码看,子类Cat有一个特有的属性String name = "tomCat"; 并且还有一个特有的抓老鼠的方法catchMouse()。但是在测试类(Demo_Test)中,我们尝试调用子类特有的方法catchMouse()和打印子类特有的成员属性String name = "tomCat"; 就会报错。
am.catchMouse();
System.out.println(am.name);
原因就是多态的弊端,就是:不能使用子类特有的成员属性和子类特有的成员方法。
如果在代码执行过程中还想使用Cat类中特有的属性String name和它特有的成员方法catchMouse()了怎么办呢?那我们就可以把这个父类引用指向了子类对象的家伙am再强制变回Cat类型。这样am就是Cat类型的引用了,指向的也是Cat对象了,自然也能使用Cat类的一切属性和一切的成员方法。
很明显,执行强转语句Cat ct = (Cat)am;之后,ct就指向最开始在堆内存中创建的那个Cat类型的对象了。这就是多态的魅力吧,虽然它有缺点,但是它确实十分灵活,减少多余对象的创建,不用说为了使用子类的某个方法又去重新再堆内存中开辟一个新的子类对象。
网友评论