*多态的概述
public class Demo9_Polymorphic {
public static void main(String[] args) {
/*
Cat a = new Cat();
a.eat();
a.catchMouse();
Dog d = new Dog();
d.eat();
d.lookHome();
*/
/*
Animal a = new Dog();
a.eat();
//a.catchMouse();
Animal a2 = new Dog();
a2.eat();
*/
method(new Dog());
method(new Dog());
}
public static void method(Cat cc) {
cc.eat();
}
}
class Animal {
public void eat() {
System.out.println("动物吃");
}
}
class Cat extends Animal{
public void eat() {
System.out.println("鱼");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal{
public void eat() {
System.out.println("肉");
}
public void lookHome() {
System.out.println("看家");
}
}
1.动态绑定:是指"在执行期间"(而非编译期间)判断所引用对象的实际类型,根据实际类型调用相应的方法
下面就用内存图解释这一点:先看上面代码,再看内存图
1.1 Animal a = new Cat(); 指的是:父类类型的引用指向子类对象. 当代码执行到这一句时,父类会先进内存(这就是前面学到的用到就要加载到内存),然后子类也就是Cat类也加载进入内存.
1.2 子类从父类继承来的成员变量(属性)全都是存在隐藏的super内存里面(其实我觉得继承来的方法,以地址的形式也存在在这里面--冯佳没讲方法).剩余其他部分也就是属于this了.
1.3 Animal a 只能看到super里面的,也就是父类里面的,而看不到this里面的东西.重点来了,编译的时候会先看看父类的eat()方法,为什么呢???因为a是Animal类型的,所以编译的时候super下面的那个线会指向Animal类里的eat方法.在运行的时候这个时候就是动态绑定了他会指向子类Cat里面,看看子类有没有重写eat方法,如果子类里面没有重写eat方法就执行父类里面的eat方法,如果子类里面重写了eat方法就执行子类里面的eat方法.
1.4 怎么验证1.3的说法呢?把父类里面的eat方法给去掉,立马就会报错(这种错误属于编译错误)说找不到符号.--冯佳
1.5 解释(1.动态绑定):所引用对象的实际类型是什么?我们是用那个类创建的对象,是Cat类.所以实际类型就是Cat类,所以动态绑定执行的时候就会去Cat类里面去eat方法.
1.6 根据这个图:编译看左边运行看右边.(针对方法)--冯佳
1.7 编译看左边,运行也看左边(针对变量.代码重叠)--佳庆
编译看左边父类:如果父类没有这个变量,编译时期,语法报错
运行也看左边父类:运行时,真正执行的是父类的变量
2.多态的好处和弊端
1.7问题: 能不能执行a.catchMouse();方法呢? 答:不能. 为什么?因为父类里面没有编译就通不过.
1.8问题: 父类的引用指向子类对象这样创建对象,子类的特有方法就没办法执行了就如下面代码:
Animal a = new Cat();
a.eat();
Animal a2 = new Dag();
a2.eat();
要通过这种方式去调用eat()方法太麻烦了--->>所以就再创建一个方法.--->>在方法里调用eat();
public static void method(Cat cc) {
cc.eat();
}
怎么去调用method方法呢?下面这样调用
method(new Cat());
//就相当于 Cat cc = new Cat();
//这样就可以调用eat()方法了
但是method(new Dog());又不行了
method(new Dog());又不行了,问题还是没有解决啊?还是没有简便啊?
所以把括号里面的Cat改成Animal问题就解决了.
public static void method(Animal cc) {
cc.eat();
}
现在下面的代码全部都可以执行了,这就是多态的好处,提高了代码的扩展性.
method(new Cat());
method(new Dog());
总结1:多态的需要具备的3个条件
-
父类引用指向子类对象
-
要有继承(类)或者实现(接口)
-
要有重写
总结2:好处和弊端
1. 多态的好处,提高代码的扩展性
2. 多态的弊端,不能调用子类的特有方法
3.向上转型和向下转型
public class Demo9_Polymorphic {
public static void main(String[] args) {
Animal a = new Cat();//这叫向上转型
a.eat();
Cat c = (Cat)a;//这叫向下转型
c.catchMouse();
}
}
}
class Animal {
public void eat() {
System.out.println("动物吃");
}
}
class Cat extends Animal{
public void eat() {
System.out.println("鱼");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal{
public void eat() {
System.out.println("肉");
}
public void lookHome() {
System.out.println("看家");
}
}
Cat c = (Cat)a;//这叫向下转型
解释:这句代码并没有创建对象,而是把0*0022地址给了c.所以c也就指向了整个对象,也可以说this所在的内存.其实super里面的也可以调用,全部都可以我自己试了.
重点:再说一遍a只能指向super所在的内存.
总结:
-
多态自始至终都是子类对象在做着变化
-
为了让大家更加清晰地了解多态,举例超人的例子