多态:同一个对象(事物),在不同时刻体现出来的不同状态。
例:猫是猫,猫是动物。 水(液体,固体,气态)。
1.多态的好处:
A:提高了代码的维护性(继承保证)
B:提高了代码的扩展性(由多态保证)
对象间的转型问题:
向上转型:从子到父
Fu f = new Zi();
向下转型:从父到子
Zi z = (Zi)f;
1.多态的前提
1.继承
2.要有方法重写(相同方法)
3.父类应用指向子类对象 父 f = new 子();
class Fu{
public void show(){
System.out.println(show Fu);
}
}
class Son extends Fu{ //继承是多态的前提
public void show(){ //重写父类方法
System.out.println(show son);
}
}
class DuotaiDemo{
public static void main(String[] args){
Fu f = new Son(); //父类引用指向自类对象。
}
}
2.多态中成员访问特点
f.show();
编译的时候都要看父类中有没有相关的成员变量,成员方法,静态方法,没有会报错;出现被重写的成员方法时,运行的结果就是子类中的内容。
1.成员变量
[编译看左边,运行看左边。]
父类引用只能访问父类成员变量。
2.构造方法
创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化。
3.成员方法
[编译看左边,运行看右边。]
父类引用
只有被方法重写了之后,运行才是子类中的内容;成员变量没有重写,静态方法和类相关,算不上重写,所以都是访问左边。
4.静态方法
编译看左边,运行看左边。
(静态和类相关,算不上重写,所以,访问还是左边的)
由于成员方法存在方法重写,所以它运行看右边。
- 多态的 成员方法,成员变量
由于多态的成员比哪里和和成员方法存在编译看左边,运行看右边的特性,所以使用多态来访问的成员变量和成员方法都必须要重写父类的(也就是父类和子类中都有相同的成员变量和成员方法)。
这个也是多态的弊端不能使用子类的特有功能。
3.多态的扩展性体现:
被延续的代码:
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("猫趴着睡觉"); }
}
//扩展第n只动物......
- 没有使用多态的普通版。
..... 延续上面的
//针对动物操作的工具类,工具类不建议修改。帮助子类对象更加方便的调用子类方法。
class AnimalTool {
private AnimalTool(){} //私有无参构造方法,使类不能创建对象
public static void useCat(Cat c) { //静态方法,不创建对象,直接用类进行调用。
c.eat();
c.sleep();
}
//扩展第二只动物的操作方法。。。。。
public static void useDog(Dog d) { d.eat(); d.sleep(); }
//扩展第n只动物的操作方法.........。。。。
}
//测试类。
class DuoTaiDemo2 {
public static void main(String[] args) {
Cat c = new Cat();
Cat c2 = new Cat();
AnimalTool.useCat(c);
AnimalTool.useCat(c2);
Dog d = new Dog();
Dog d2 = new Dog();
AnimalTool.useDog(d);
AnimalTool.useDog(d2);
}
}
从上面这些代码可以看出,当子类不断增加的时候,导致操作子类的工具类也在不断添加新的子类操作方法,但是工具类是操作子类方法的,所以不建议频繁修改,
- 多态 [扩展性]
class AnimalTool {
private AnimalTool(){} //使类不能创建对象
public static void useAnimal(Animal a) { //传入父类引用
a.eat();
a.sleep();
}
}
class DuoTaiDemo2 {
public static void main(String[] args) {
Cat c = new Cat();
Cat c2 = new Cat();
AnimalTool.useAnimal(c); //子类引用等于父类引用,实现了多态的要求。
AnimalTool.useAnimal(c2);
Dog d = new Dog();
Dog d2 = new Dog();
AnimalTool.useAnimal(d);
AnimalTool.useAnimal(d2);
}
4.多态的弊端:
- 不能使用子类特有的功能:
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("show zi method");}
}
//延续代码001
class DuotaiDemo{
public static void main(String[] args){
Fu f = new Zi();
f.show();
f.method(); //报错,不能使用子类中的特有功能。
}
}
- 向下转型使用子类特有功能。
//.....延续代码001
class DuotaiDemo{
public static void main(String[] args){
Fu f = new Zi();
Zi z = (Zi)f; //Zi z = f 表示把父的引用赋值值给子的引用。使用(Zi)进行强转。
f.show();
z.method(); //可以使用子类特有功能。
}
}
没使用向下转型之前,父类的引用指向子类对象,编译时父所调用的方法必须在父类中存在(子类中重写),否则会报错。
使用向下转型之后,将父类的引用强制转换为子类的引用,此时编译的时候,子类调用的方法只需要在子类中存在即可,实现向下转型使用子类特有功能。
5.多态的转型问题
Java培训特别火,很多人来请孔子爹去讲课,这一天孔子爹被请走了,但是还有人来请,就剩孔子在家,价格还挺高。孔子一想,然后就穿上爹的衣服,带上爹的眼睛,粘上爹的胡子。就开始装爹
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("英雄联盟");
}
}
//向上转型
孔子爹 k爹 = new 孔子(); //孔子看穿着爹的衣服,看起来像爹。
//到人家那里去了
System.out.println(k爹.age); //40 外表看起来就是爹的年龄
k爹.teach(); //虽然长得像爹,但是他只会讲解论语
//k爹.playGame(); 由于长得想爹,所以虽然本质上是儿子,但是也不能使用儿子的功能。
//向下转型
孔子 k = (孔子) k爹; //讲完了,下班回家了,脱下爹的装备,换上自己的装备
System.out.println(k.age); //现在看起来年级就是20
k.teach(); //他在做他自己,所以讲解论语
k.playGame(); //所以也能玩游戏英雄联盟
6.多态练习:
- 不同地方饮食文化不同的案例
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.jingShang();
p = new NorthPerson(); //北方人
p.eat();
NorthPerson np = (NorthPerson)p;
np.yanJiu();
}
}
- 猫狗练习
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();
//编译不报错,运行时候报错。此时不能再强制转换了,因为此时a是猫的引用,所以不能强制转换为狗的引用。
//Dog dd = (Dog)a;
}
}
- 多态的一个综合案例
继承的时候:
子类中有和父类中一样的方法,叫重写。
子类中没有父亲中出现过的方法,方法就被继承过来了。
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("你"); }
}
class DuoTai {
public static void main(String[] args) {
A a = new B();
a.show(); //子类没有,继承父类show方法,此时继承过来的show方法中调用的show2方法就是B中的show2方法。
B b = new C();
b.show(); //子类c覆盖(重写)父类B的show方法,通过super.show()调用父类show方法,父类的show方法中调用的是show2方法,此时show2方法就是C中的show2。
}
}
- no
/*
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
}
}