多态(掌握)
(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(); //你
}
}