面向对象之多态
先给大家讲个笑话
多态的引入:
生活中的多态
在生活中我们听到“打”一声可能只是 击,敲,攻击的意思,但是其实在不同的环境下,打的意思非常多。
例如:打车,打电话,打雷,打证明,打包裹
多态的概述
生活中: 同一个动作在不同环境下表现出来的不同状态
Java中: 同一个方法在不同的对象中体现出来不同的状态
内存中: 父类引用指向子类对象
多态的实现的必要条件:(重点要记下)
1.存在继承关系
2.存在方法重写
3.父类引用指向子类对象
先给个小例子代码如下所示:
public class DuoTaiDemo01 {
public static void main(String[] args) {
Man m = new Doctor();
m.cut();
m = new Director();
m.cut();
m = new Hairdresser();
m.cut();
}
}
class Man {
public void cut() {
System.out.println("我是man, 我也不知道怎么cut");
}
}
class Doctor extends Man {
public void cut() {
System.out.println("动手术");
}
}
class Director extends Man {
@Override
public void cut() {
System.out.println("暂停");
}
}
class Hairdresser extends Man {
@Override
public void cut() {
System.out.println("剪头发");
}
}
多态访问成员的特点:
Father father = new Son()
左边类型 ————右边类型
成员变量:
1.编译时期看左边的类型,如果左边类型中没有变量,编译报错
2.运行时期看左边类型,左边类型的变量的值就是运行的结果
3.编译看左边,执行看左边
成员方法:
编译看左边,执行看右边
构造方法:
1.多态访问子类构造方法会先访问父类构造方法
2.帮助子类初始化父类继承过来的成员
静态方法:
编译看左边,执行看左边
举例代码如下:
public class DuoTaiDemo02 {
public static void main(String[] args) {
Fu fu = new Zi();
System.out.println(fu.num); // 10
fu.method();
fu.show();
}
}
class Fu {
int num = 10;
public void method() {
System.out.println("Fu.method()");
}
public static void show() {
System.out.println("Fu.show");
}
}
class Zi extends Fu {
int num = 20;
@Override
public void method() {
System.out.println("Zi.method()");
}
public static void show() {
System.out.println("Zi.show");
}
}
多态的优点
1.简化了代码
2.提高了维护性和扩展性
//饲养员给不同的动物喂不同的食物
public class DuoTaiDemo03 {
public static void main(String[] args) {
Feeder f = new Feeder();
Meat m = new Meat();
m.setName("五花肉");
Tiger t = new Tiger();
t.setName("东北虎");
// f.feedMeatToTiger(m, t);
f.feed(m, t);
Bamboo b = new Bamboo();
b.setName("大竹子");
Panda p = new Panda();
p.setName("大熊猫");
// f.feedBambooToPanda(b, p);
f.feed(b, p);
Food food = new Banana();
food.setName("香蕉");
Animals an = new Monkey();
an.setName("猴子");
f.feed(food, an);
}
}
//开闭原则: 对扩展开放,对修改关闭
class Feeder{
// 给老虎喂肉
// public void feedMeatToTiger(Meat m, Tiger t) {
// m.show();
// t.show();
// }
// 熊猫喂竹子
// public void feedBambooToPanda(Bamboo b, Panda p) {
// b.show();
// p.show();
// }
// 利用多态
/*
* Food f = new Meat();
* Animals a = new Tiger();
*/
public void feed(Food f, Animals a) {
f.show();
a.show();
}
}
class Animals{
public String name;
public void show() {
System.out.println("我是动物");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Monkey extends Animals {
@Override
public void show() {
System.out.println("我是" + getName());
}
}
class Banana extends Food {
@Override
public void show() {
System.out.println("我是" + getName());
}
}
class Bear extends Animals{
@Override
public void show() {
System.out.println("我是" + name);
}
}
class Tiger extends Animals{
@Override
public void show() {
System.out.println("我是" + name);
}
}
class Panda extends Animals{
@Override
public void show() {
System.out.println("我是" + name);
}
}
class Food{
public String name;
public void show() {
System.out.println("我是食物");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Meat extends Food{
public void show() {
System.out.println("我是" + name);
}
}
class Bee extends Food{
@Override
public void show() {
System.out.println("我是" + name);
}
}
class Bamboo extends Food{
public void show() {
System.out.println("我是" + name);
}
}
多态的缺点:
使用父类引用无法访问子类所特有的方法
解决办法: 向下转型
基本类型存在自动类型转换和强制类型转换
引用类型存在向上转型和向下转型
向上转型(自动转换)
格式:<父类型> <引用变量名> = new <子类型>();
特点:
子类转为父类 父类的引用指向子类对象。可以理解为自动进行类型转换(和自动类型转换完全是两个概念)
此时通过父类引用变量调用的方法是子类覆盖或继承父类的方法
此时通过父类引用变量无法调用子类特有的属性和方法
向下转型(强制转换)
格式:<子类型> <引用变量名> = (<子类型> )<父类型的引用变量>;
特点:
父类转为子类,父类引用转为子类对象。可以理解为强制类型转换
在向下转型的过程中,如果没有转换为真实子类类型,会出现类型转换异常
异常名称: 类型转换异常 java.lang.ClassCastException
产生原因: 在向下转型的过程中,没有转换成真实的类型
解决办法: 在每次向下转型之前做一个类型的判断
类型判断的语法: instanceof
左边对象 instanceof 类名 这个表达式的结果是boolean类型
测试它左边的对象是否是它右边的类的实例
多态的弊端可以使用instanceof关键字+向下转型来解决
我们知道我们需要对父类的所有子类做逐一判断,违背了开闭原则
为了开闭原则我们还是可以继续开发,但是如果这个父类引用是Object呢?
无法做逐一个判断,安全隐患一致存在,可以考虑是泛型。
举例1代码如下所示:
public class DuoTaiDemo04 {
public static void main(String[] args) {
// Car c = new BMW();
// c.run();
// 想要访问BMW的fillOil,无法访问
// 利用向下转型
// BMW bmw = (BMW) c;
// bmw.fillOil();
//
// c = new Benz();
// c.run();
// Benz benz = (Benz) c;
// benz.leakOli();
// BMW bmw2 = (BMW) c;
// bmw2.fillOil();
// System.out.println(c instanceof BMW);
// System.out.println(c instanceof Benz);
Car c = new Benz();
c.run();
c = new BYD();
c.run();
if (c instanceof Benz) {
Benz benz = (Benz) c;
benz.leakOli();
} else if (c instanceof BMW) {
BMW b = (BMW) c;
b.fillOil();
} else if (c instanceof BYD) {
BYD byd = (BYD) c;
byd.electric();
}
Object obj = new BMW();
}
}
class Car {
public void run() {
System.out.println("Car.run()");
}
}
class BMW extends Car {
@Override
public void run() {
System.out.println("BMW.run()");
}
public void fillOil() {
System.out.println("加油");
}
}
class Benz extends Car {
@Override
public void run() {
System.out.println("Benz.run()");
}
public void leakOli() {
System.out.println("漏油");
}
}
class BYD extends Car {
@Override
public void run() {
System.out.println("BYD.run()");
}
public void electric() {
System.out.println("充电");
}
}
举例2代码如下所示:
/*
实现主人与宠物玩耍功能 play
和狗狗玩接飞盘游戏。
和企鹅玩游泳游戏。
编写测试类测试
分析:
给Dog添加接飞盘方法catchingFlyDisc( )
给Penguin添加游泳方法swimming( )
给主人添加play(Pet pet)方法 sendPet(Pet pet)
思考:如果还需要再主人类中添加赠送宠物的方法呢?
*/
public class DuoTaiDemo05 {
public static void main(String[] args) {
Hoster hoster = new Hoster(5, 3, "老王");
hoster.play(new Dog());
hoster.play(new Penguin());
hoster.sendPet(new Dog());
hoster.sendPet(new Dog());
hoster.sendPet(new Dog());
hoster.sendPet(new Dog());
hoster.sendPet(new Dog());
hoster.sendPet(new Dog());
System.out.println(hoster.getPenguinCount());
hoster.sendPet(new Penguin());
hoster.sendPet(new Penguin());
hoster.sendPet(new Penguin());
hoster.sendPet(new Penguin());
}
}
class Hoster {
private int dogCount;
private int penguinCount;
private String name;
public Hoster() {
super();
}
public Hoster(int dogCount, int penguinCount, String name) {
super();
this.dogCount = dogCount;
this.penguinCount = penguinCount;
this.name = name;
}
public void play(Pet pet) {
pet.play();
if (pet instanceof Dog) {
Dog dog = (Dog) pet;
dog.catchingFlyDisc();
} else if (pet instanceof Penguin) {
Penguin penguin = (Penguin) pet;
penguin.swimming();
}
}
public void sendPet(Pet pet) {
if (pet instanceof Dog) {
dogCount --;
if (dogCount < 0) {
dogCount = 0;
}
System.out.println("还剩下" + dogCount + "只狗!!!");
} else if (pet instanceof Penguin) {
penguinCount --;
if (penguinCount < 0) {
penguinCount = 0;
}
System.out.println("还剩下" + penguinCount + "只企鹅!!!");
}
}
public int getDogCount() {
return dogCount;
}
public void setDogCount(int dogCount) {
this.dogCount = dogCount;
}
public int getPenguinCount() {
return penguinCount;
}
public void setPenguinCount(int penguinCount) {
this.penguinCount = penguinCount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Pet {
public void play() {
System.out.println("Pet.play()");
}
}
class Dog extends Pet {
public void play() {
System.out.println("Dog.play()");
}
public void catchingFlyDisc() {
System.out.println("Dog.catchingFlyDisc()");
}
}
class Penguin extends Pet {
@Override
public void play() {
System.out.println("Penguin.play()");
}
public void swimming() {
System.out.println("Penguin.swimming()");
}
}