多态(掌握)
(1)同一个对象在不同时刻体现出来的不同状态。
(2)多态的前提:
A:有继承或者实现关系。
B:有方法重写。
C:有父类或者父接口引用指向子类对象。
多态的分类:
a:具体类多态
class Fu {}
class Zi extends Fu {}
Fu f = new Zi();
b:抽象类多态
abstract class Fu {}
class Zi extends Fu {}
Fu f = new Zi();
c:接口多态
interface Fu {}
class Zi implements Fu {}
Fu f = new Zi();
(3)多态中的成员访问特点
A:成员变量
编译看左边,运行看左边
B:构造方法
子类的构造都会默认访问父类构造
C:成员方法
编译看左边,运行看右边
D:静态方法
编译看左边,运行看左边
/* 多态:同一个对象(事物),在不同时刻体现出来的不同状态。 举例: 猫是猫,猫是动物。 水(液体,固体,气态)。 多态的前提: A:要有继承关系。 B:要有方法重写。 其实没有也是可以的,但是如果没有这个就没有意义。 动物 d = new 猫(); d.show(); 动物 d = new 狗(); d.show(); C:要有父类引用指向子类对象。 父 f = new 子(); 用代码体现一下多态。 多态中的成员访问特点:变量没有重写,用的还是父亲的;因为方法有重写,父类方法被子类覆盖掉了,只留下子类的方法 A:成员变量 编译看左边,运行看左边。 B:构造方法 创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化。 C:成员方法 编译看左边,运行看右边。 D:静态方法 编译看左边,运行看左边。 (静态和类相关,算不上重写,所以,访问还是左边的) 由于成员方法存在方法重写,所以它运行看右边。 */ public class Fu { public int num = 100; public void show() { System.out.println("show Fu"); } public static void function() { System.out.println("function Fu"); } } public class Zi extends Fu { public int num = 1000; public int num2 = 200; public void show() { System.out.println("show Zi"); } public void method() { System.out.println("method zi"); } public static void function() { System.out.println("function Zi"); } } public class DuoTaiDemo { public static void main(String[] args) { //要有父类引用指向子类对象。 //父 f = new 子(); Fu f = new Zi(); System.out.println(f.num); //100 //System.out.println(f.num2);//报错:找不到符号 f.show(); //show Zi //f.method(); //报错:找不到符号 f.function(); //function Fu } } (4)多态的好处:A:提高代码的维护性(继承体现)
B:提高代码的扩展性(多态体现)
/* 多态的好处: A:提高了代码的维护性(继承保证) B:提高了代码的扩展性(由多态保证) 猫狗案例代码 */ class Animal { public void eat(){ System.out.println("eat"); } public void sleep(){ System.out.println("sleep"); } } class Dog extends Animal { public void eat(){ System.out.println("狗吃肉"); } public void sleep(){ System.out.println("狗站着睡觉"); } } class Cat extends Animal { public void eat() { System.out.println("猫吃鱼"); } public void sleep() { System.out.println("猫趴着睡觉"); } } class Pig extends Animal { public void eat() { System.out.println("猪吃白菜"); } public void sleep() { System.out.println("猪侧着睡"); } } //针对动物操作的工具类 class AnimalTool { private AnimalTool(){} /* //调用猫的功能 public static void useCat(Cat c) { c.eat(); c.sleep(); } //调用狗的功能 public static void useDog(Dog d) { d.eat(); d.sleep(); } //调用猪的功能 public static void usePig(Pig p) { p.eat(); p.sleep(); } */ public static void useAnimal(Animal a) { a.eat(); a.sleep(); } } class DuoTaiDemo2 { public static void main(String[] args) { //我喜欢猫,就养了一只 Cat c = new Cat(); c.eat(); c.sleep(); //我很喜欢猫,所以,又养了一只 Cat c2 = new Cat(); c2.eat(); c2.sleep(); //我特别喜欢猫,又养了一只 Cat c3 = new Cat(); c3.eat(); c3.sleep(); //... System.out.println("--------------"); /* 问题来了,我养了很多只猫,每次创建对象是可以接受的 但是呢?调用方法,你不觉得很相似吗?仅仅是对象名不一样。 我们准备改进 */ //useCat(c); //useCat(c2); //useCat(c3); //AnimalTool.useCat(c); //AnimalTool.useCat(c2); //AnimalTool.useCat(c3); AnimalTool.useAnimal(c); AnimalTool.useAnimal(c2); AnimalTool.useAnimal(c3); System.out.println("--------------"); //我喜欢狗 Dog d = new Dog(); Dog d2 = new Dog(); Dog d3 = new Dog(); //AnimalTool.useDog(d); //AnimalTool.useDog(d2); //AnimalTool.useDog(d3); AnimalTool.useAnimal(d); AnimalTool.useAnimal(d2); AnimalTool.useAnimal(d3); System.out.println("--------------"); //我喜欢宠物猪 //定义一个猪类,它要继承自动物,提供两个方法,并且还得在工具类中添加该类方法调用 Pig p = new Pig(); Pig p2 = new Pig(); Pig p3 = new Pig(); //AnimalTool.usePig(p); //AnimalTool.usePig(p2); //AnimalTool.usePig(p3); AnimalTool.useAnimal(p); AnimalTool.useAnimal(p2); AnimalTool.useAnimal(p3); System.out.println("--------------"); /* 我喜欢宠物狼,老虎,豹子... 定义对应的类,继承自动物,提供对应的方法重写,并在工具类添加方法调用 前面几个必须写,我是没有意见的 但是,工具类每次都改,麻烦不 我就想,你能不能不改了 太简单:把所有的动物都写上。问题是名字是什么呢?到底哪些需要被加入呢? 改用另一种解决方案。 public static void useAnimal(Animal a) { a.eat(); a.sleep(); } */ } }(5)多态的弊端:
父不能使用子的特有功能。
现象:
子可以当作父使用,父不能当作子使用。
/* 多态的弊端: 不能使用子类的特有功能。 */ class Fu { public void show() { System.out.println("show fu"); } } class Zi extends Fu { public void show() { System.out.println("show zi"); } public void method() { System.out.println("method zi"); } } class DuoTaiDemo3 { public static void main(String[] args) { //测试 Fu f = new Zi(); f.show(); //show zi f.method();//报错:方法未定义 } } (6)多态中的转型A:向上转型
从子到父,父类引用指向子类对象:Fu f = new Zi();
B:向下转型
从父到子,父类引用转为子类对象:Zi z = (Zi)f;
/* 多态的弊端: 不能使用子类的特有功能。 我就想使用子类的特有功能?行不行? 行。 怎么用呢? A:创建子类对象调用方法即可。(可以,但是很多时候不合理。而且,太占内存了) B:把父类的引用强制转换为子类的引用。(向下转型) 对象间的转型问题: 向上转型: Fu f = new Zi(); 向下转型: Zi z = (Zi)f; //要求该f必须是能够转换为Zi的。 */ class Fu { public void show() { System.out.println("show fu"); } } class Zi extends Fu { public void show() { System.out.println("show zi"); } public void method() { System.out.println("method zi"); } } class DuoTaiDemo4 { public static void main(String[] args) { //测试 Fu f = new Zi(); f.show(); //show zi //f.method();//报错:方法未定义 //创建子类对象,可以,但是很多时候不合理。而且,太占内存了。 //Zi z = new Zi(); //z.show(); //z.method(); //你能够把子的对象赋值给父亲,那么我能不能把父的引用赋值给子的引用呢? //如果可以,代码如下: //向下转型 Zi z = (Zi)f; z.show(); //show zi z.method();//method zi } } (7)孔子装爹的案例帮助大家理解多态 多态的问题理解: class 孔子爹 { public int age = 40; public void teach() { System.out.println("讲解JavaSE"); } } class 孔子 extends 孔子爹 { public int age = 20; public void teach() { System.out.println("讲解论语"); } public void playGame() { System.out.println("英雄联盟"); } } //Java培训特别火,很多人来请孔子爹去讲课,这一天孔子爹被请走了 //但是还有人来请,就剩孔子在家,价格还挺高。孔子一想,我是不是可以考虑去呢? //然后就穿上爹的衣服,带上爹的眼睛,粘上爹的胡子。就开始装爹 //向上转型 孔子爹 k爹 = new 孔子(); //到人家那里去了 System.out.println(k爹.age); //40 System.out.println(k爹.teach()); //讲解论语 //k爹.playGame(); //这不行,这是儿子才能做的 //讲完了,下班回家了,脱下爹的装备,换上自己的装备 //向下转型 孔子 k = (孔子) k爹; System.out.println(k.age); //20 k.teach(); //讲解论语 k.playGame(); //英雄联盟 补充:多态继承中的内存图解补充:一般在多态的向下转型中容易出现ClassCastException:类型转换异常
/* ClassCastException:类型转换异常 一般在多态的向下转型中容易出现 */ class Animal { public void eat(){} } class Dog extends Animal { public void eat() {} public void lookDoor() { } } class Cat extends Animal { public void eat() { } public void playGame() { } } class DuoTaiDemo5 { public static void main(String[] args) { //内存中的是狗 Animal a = new Dog(); Dog d = (Dog)a; //内存中是猫 a = new Cat(); Cat c = (Cat)a; //内存中是猫 Dog dd = (Dog)a; //ClassCastException } }
(8)多态的练习
/* 多态练习:猫狗案例 */ class Animal { public void eat(){ System.out.println("吃饭"); } } class Dog extends Animal { public void eat() { System.out.println("狗吃肉"); } public void lookDoor() { System.out.println("狗看门"); } } class Cat extends Animal { public void eat() { System.out.println("猫吃鱼"); } public void playGame() { System.out.println("猫捉迷藏"); } } class DuoTaiTest { public static void main(String[] args) { //定义为狗 Animal a = new Dog(); a.eat();//狗吃肉 //还原成狗 Dog d = (Dog)a; d.eat(); //狗吃肉 d.lookDoor();//狗看门 //变成猫 a = new Cat(); a.eat();//猫吃鱼 //还原成猫 Cat c = (Cat)a; c.eat(); //猫吃鱼 c.playGame();//猫捉迷藏 //演示错误的内容 //Dog dd = new Animal();//不兼容的类型 //Dog ddd = new Cat(); //不兼容的类型 //Dog dd = (Dog)a; //ClassCastException } } /* 不同地方饮食文化不同的案例 */ class Person { public void eat() { System.out.println("吃饭"); } } class SouthPerson extends Person { public void eat() { System.out.println("炒菜,吃米饭"); } public void jingShang() { System.out.println("经商"); } } class NorthPerson extends Person { public void eat() { System.out.println("炖菜,吃馒头"); } public void yanJiu() { System.out.println("研究"); } } class DuoTaiTest2 { public static void main(String[] args) { //测试 //南方人 Person p = new SouthPerson(); p.eat(); //炒菜,吃米饭 SouthPerson sp = (SouthPerson)p; sp.eat(); //炒菜,吃米饭 sp.jingShang(); //经商 //北方人 p = new NorthPerson(); p.eat(); //炖菜,吃馒头 NorthPerson np = (NorthPerson)p; np.eat(); //炖菜,吃馒头 np.yanJiu(); //研究 } } 看程序写结果: /* 看程序写结果:先判断有没有问题,如果没有,写出结果 多态的成员访问特点: 方法:编译看左边,运行看右边。 继承的时候: 子类中有和父类中一样的方法,叫重写。 子类中没有父亲中出现过的方法,方法就被继承过来了。 */ class A { public void show() { show2(); } public void show2() { System.out.println("我"); } } class B extends A { /*可以理解存在该代码,因为继承了父类的方法 public void show() { show2(); } */ public void show2() { System.out.println("爱"); } } class C extends B { public void show() { super.show(); } public void show2() { System.out.println("你"); } } public class DuoTaiTest4 { public static void main(String[] args) { A a = new B(); a.show(); //爱 B b = new C(); b.show(); //你 } }