11 面向对象之多态
多态
-
多态,事物的多种形态
简单的理解:同一个行为具有多个不同表现形式或形态的能力。
书面概述:多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
生活中案例: 比如你是一个酒神,对酒情有独钟。某日回家发现桌上有几个杯子里面都装了白酒,从外面看我们是不可能知道这是些什么酒,只有喝了之后才能够猜出来是何种酒。你一喝,这是剑南春、再喝这是五粮液、再喝这是茅台酒,描述如下
酒 a = new 剑南春(); 酒 b = new 五粮液(); 酒 c = new 茅台酒();
-
实现多态三个必要条件:
-
父子类
剑南春、五粮液、茅台酒都是酒的子类,我们只是通过酒这一个父类就能够引用不同的子类,这就是多态。我们只有在运行的时候才会知道引用变量所指向的具体实例对象。
class 剑南春 extends 酒 class 五粮液 extends 酒 class 茅台酒 extends 酒
-
重写
子类需要重写父类方法
/** * 酒 * * 带你轻松学Java:恒骊学堂 * www.hliedu.com * QQ群:107184365 * */ class 酒 { public void wine() { System.out.println("喝酒"); } } class 剑南春 extends 酒{ public void wine() { System.out.println("喝剑南春"); } } class 五粮液 extends 酒{ public void wine() { System.out.println("喝五粮液"); } } class 茅台酒 extends 酒{ public void wine() { System.out.println("喝茅台酒"); } }
-
向上转型
父类 obj = new 子类
我们知道,我们要喝剑南春时,直接实例化剑南春类即可,非常好理解,如下
剑南春 jnc = new 剑南春(); jnc.wine();
但如果我们改为向上转型的写法呢?
酒 jnc = new 剑南春(); jnc.wine();
在这里我们这样理解,这里定义了一个
酒
类型的jnc,它指向剑南春对象实例。由于剑南春
是继承于酒
,所以剑南春
可以自动向上转型为酒
,所以jnc是可以指向剑南春
实例对象的。这样做存在一个非常大的好处,在继承中我们知道子类是父类的扩展,它可以提供比父类更加强大的功能,如果我们定义了一个指向子类的父类引用类型,那么它除了能够引用父类的共性外,还可以使用子类强大的功能。
-
-
多态表现形式
基于继承:通常采用抽象类来做,必须要有继承关系,抽象类中定义抽象方法,子类继承抽象类后覆写抽象类中的抽象方法,达到多态效果。多态子类的实例可以赋值给父类的引用
基于接口:指向接口的引用必须是指定这实现了该接口的一个类的实例程序,在运行时,根据对象引用的实际类型来执行对应的方法
-
多态案例
- 使用多态来描述动物类、猫、狗
package com.hliedu.dt2; /** * 多态案例 * * 带你轻松学Java:恒骊学堂 * www.hliedu.com * QQ:3020685261 * */ public class Demo2 { public static void main(String[] args) { Animal animal1 = new Dog(); animal1.eat(); Animal animal2 = new Dog(); animal2.eat(); } } //动物类 class Animal { public void eat() {} } //狗类 class Dog extends Animal { @Override public void eat() { System.out.println("狗吃屎"); } public void lookDoor() { System.out.println("看门"); } } //猫类 class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } public void playGame() { System.out.println("玩游戏"); } }
注意:Animal作为父类,无法访问子类(Dog,Cat)中自己定义的扩展方法
- 使用多态完成主人喂猫、狗吃东西的功能,要求喂猫和喂狗只能由一个方法完成
package com.hliedu.dt2; /** * 多态案例 * * 带你轻松学Java:恒骊学堂 * www.hliedu.com * QQ:3020685261 * */ public class Demo2 { public static void main(String[] args) { Animal animal1 = new Dog(); animal1.name = "二哈"; Animal animal2 = new Cat(); animal2.name = "二喵"; Master master = new Master(); master.feed(animal1); master.feed(animal2); } } //动物类 class Animal { String name = ""; public void eat() {} } //狗类 class Dog extends Animal { @Override public void eat() { System.out.println("狗吃屎"); } public void lookDoor() { System.out.println("看门"); } } //猫类 class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } public void playGame() { System.out.println("玩游戏"); } } class Master { public void feed(Animal animal){ System.out.println("主人开始喂" + animal.name + "吃东西"); animal.eat(); System.out.println(animal.name + "吃完了"); } }
多态内存结构
多态接口形态
向上转型存在一些缺憾,那就是它必定会导致一些方法和属性的丢失,而导致我们不能够获取它们。所以父类类型的引用可以调用父类中定义的所有属性和方法,对于只存在与子类中的方法和属性它就望尘莫及了
package com.hliedu.dt4;
/**
* 多态案例2,接口形态
* 主人喂食
*
* 带你轻松学Java:恒骊学堂
* www.hliedu.com
* QQ:3020685261
*
*/
public class Demo4 {
public static void main(String[] args) {
Animal animal1 = new Dog(10 , "二哈");
Animal animal2 = new Cat(20 , "二喵");
Master master = new Master();
master.feed(animal1);
master.feed(animal2);
}
}
//1:父子关系
//2:方法重写
//3:向上转型,父类有一个引用去指向子类
interface Animal {
void eat();
}
class Dog implements Animal{
String name;
int age;
public Dog(int age, String name) {
this.name = name;
this.age = age;
}
@Override
public void eat() {
System.out.println("吃骨头");
}
public void lookDoor() {
System.out.println(name + "看门");
}
}
class Cat implements Animal {
String name;
public int age;
public Cat(int age, String name) {
this.name = name;
this.age = age;
}
@Override
public void eat() {
System.out.println("吃鱼");
}
public void playGame() {
System.out.println(name + "玩游戏");
}
}
//主人喂食
class Master {
public void feed(Animal animal) {
Dog dog = null;
Cat cat = null;
String name = "";
//instanceof关键字
if(animal instanceof Dog) {
dog = (Dog)animal;
name = dog.name;
}else if(animal instanceof Cat) {
cat = (Cat)animal;
name = cat.name;
}
System.out.println("准备喂" + name + "吃东西");
animal.eat();
System.out.println(name + "吃完了");
if(dog != null) {
dog.lookDoor();
}else if(cat != null) {
cat.playGame();
}
}
}
多态继承链调用优先级
在继承链中对象方法的调用存在一个优先级,有优先级顺序如下
- this.show(O)
- super.show(O)
- this.show((super)O)
- super.show((super)O)
上述字符描述如下:
- this:当前类对象
- show:执行方法
- O:对象参数
- super:父类
如下代码输出什么?
package com.hliedu.dt5;
/**
* 多态继承调用链
* 带你轻松学Java:恒骊学堂
* www.hliedu.com
* QQ:3020685261
*
*/
public class Demo5 {
public static void main(String[] args) {
Parent p1 = new Parent();
Parent p2 = new ChildA();
ChildA a = new ChildA();
ChildB b = new ChildB();
p1.show(a);//①
p1.show(b);//②
p2.show(a);//③
p2.show(b);//④
a.show(a);//⑤
a.show(b);//⑥
b.show(a);//⑦
b.show(b);//⑧
}
}
class Parent {
public void show(Parent obj) {
System.out.println("Parent中的Parent类型参数");
}
public void show(ChildB obj) {
System.out.println("Parent中的ChildB类型参数");
}
}
class ChildA extends Parent {
public void show(Parent obj) {
System.out.println("ChildA中的Parent类型参数");
}
public void show(ChildA obj) {
System.out.println("ChildA中的ChildA类型参数");
}
}
class ChildB extends ChildA{
}
当超类对象的引用变量 引用子类对象时,被引用对象的类型(而不是引用变量的类型)决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。 (但是如果强制把超类转换成子类的话,就可以调用子类中新添加而超类没有的方法了)
课后作业
- 将生活中的某个种类使用多态的方式描写出来,并且区分出不一样的行为
- 举例至少3个重写与重载的例子,写出重载和重写的各自规则(面试题)