多态(面向对象编程三大特征之一)
一、引入
请编写一个程序,Master主人类中有一个feed(喂食)方法,可以完成主人给动物喂食物的信息。不同动物吃不同食物
Food(父类):Fish Bone Rice
Animals(父类):Cat Dog Pig
传统方法解决:
package poly_;
public class Poly01 {
public static void main(String[] args) {
Master tom = new Master("Tom");
Dog dog = new Dog("大黄");
Bone bone = new Bone("大棒骨");
tom.feed(dog, bone);
System.out.println("==============");
Cat cat = new Cat("花猫");
Fish fish = new Fish("黄花鱼");
tom.feed(cat, fish);
}
}
//食物类
class Food {
private String name;
public Food(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Fish extends Food {
public Fish(String name) {
super(name);
}
}
class Bone extends Food {
public Bone(String name) {
super(name);
}
}
//动物类
class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
}
//主人类
class Master {
private String name;
public Master(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void feed(Dog dog, Bone bone) {
//主人给狗喂骨头
System.out.println("主人"+this.name+"给"+dog.getName()+"吃"+bone.getName());
}
public void feed(Cat cat, Fish fish) {
//主人给猫喂鱼
System.out.println("主人"+this.name+"给"+cat.getName()+"吃"+fish.getName());
}
}
如果动物增加,食物增加
Pig–>Rice
Tiger–>Meat
===> feed方法很多,不利于管理和维护
问题是:代码的复用性不高,而且不利于代码维护
解决方案:引出我们要讲解的多态
二、多态介绍
多[多种]态[状态]基本介绍 :方法或对象具有多种形态。
多态是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
1、方法的多态
重写和重载体现多态。
2、对象的多态
编译:javac,运行:java
(1) 一个对象的编译类型和运行类型可以不一致
Animal animal = new Dog();
父类的引用指向子类的对象。
父类Animal 的引用animal 指向子类Dog 的对象。
(2) 编译类型在定义对象时,就确定了,不能改变
Animal animal = new Dog(); 【animal编译类型是Animal,运行类型Dog】
animal = new Cat(); 【animal的运行类型变成了Cat,编译类型仍然是Animal】
(3) 运行类型是可以变化的
(4) 编译类型看定义时 = 号的左边,运行类型看 = 号的右边
- 示例
//体验对象多态特点
public class Poly02_objpoly {
public static void main(String[] args) {
// animal的编译类型在定义对象时,就确定了-> 是 = 号的左边的Animal类
Animal animal = new Dog();//animal编译类型是Animal,运行类型Dog
animal.cry();//因为运行时,即 执行到该行时,animal运行类型是Dog
System.out.println("======================");
animal = new Cat();//animal编译类型是Animal,运行类型Cat
animal.cry();
}
}
class Animal {
public void cry() {
System.out.println("动物喊叫");
}
}
class Cat extends Animal {
public void cry() {
System.out.println("喵喵喵");
}
}
class Dog extends Animal {
public void cry() {
System.out.println("汪汪汪");
}
}
- 解决前文的主人喂食问题
//使用多态机制,可以统一的管理主人喂食的问题
public void feed(Animal animal, Food food) {
System.out.println("主人"+this.name
+"给"+animal.getName()+"吃"+food.getName());
}
animal 的编译类型是Animal,可以指向(接受)Animal 子类的对象。
food 的编译类型是Food,可以指向(接受)Food子类的对象。
三、注意事项&细节
1、多态的前提
多态的前提是两个对象(类)存在继承关系。
2、多态的向上转型
- 本质:父类的引用指向了子类的对象
- 语法:父类类型引用名 = new子类类型()
Animal animal = new Cat();
Object obj = new Cat();//可以吗? 可以 Object 也是 Cat 的父类 - 特点:编译类型看左边,运行类型看右边
- 向上转型调用方法的规则如下:
(1) 可以调用父类中的所有成员(需遵守访问权限)
(2) 但是不能调用子类的特有的成员
(#)因为在编译阶段,能调用哪些成员,是由编译类型来决定的
(3) 最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法,然后调用,规则我前面我们讲的方法调用规则一致。
- 示例
Animal animal = new Cat();
animal.eat();
先在Cat中寻找有没有eat方法,如果没有,就找Cat类的父类->>
Animal animal = new Cat();
animal.catchMouse();错误
如果父类 animal 类中没有catchMouse(); 只有子类Cat类中有。那么animal.catchMouse();语句是错误的。
不能调用子类的特有的成员。
3、多态的向下转型
接上文,如果animal非要访问catchMouse方法怎么办。
- 语法:子类类型引用名 = (子类类型)父类引用
- 只能强转父类的引用,不能强转父类的对象
对象是在堆中new出来的,开辟了空间、分配了地址,是无法强制转换的。
引用是在栈中的,存放了堆中对象的地址,是可以修改引用的指向的。 - 要求父类的引用必须指向的是当前目标类型的对象
- 当向下转型后,就可以调用子类类型中所有的成员
- 举例
Animal animal = new Cat();
Cat cat = (Cat) animal;
cat.catchMouse();
cat的编译类型是Cat,运行类型是Cat
解释《要求父类的引用必须指向的是当前目标类型的对象》
4、属性重写问题
属性没有重写之说!属性的值看【编译】类型 。
- 举例
Base sub = new Sub();
sub.count 是什么?
public class PolyDetails02 {
public static void main(String[] args) {
Base sub = new Sub();
System.out.println(sub.count);
}
}
class Base {
int count = 10;
}
class Sub extends Base {
int count = 20;
}
instanceOf 比较操作符
instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型 或 XX类型的子类型。
(1)对象bb是BB类型?或是BB类型的子类吗?
public class PolyDetails03 {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb instanceof BB);
}
}
class AA {}
class BB extends AA {}
:true
(2)对象bb是AA类型?或是AA类型的子类吗?
System.out.println(bb instanceof AA);
:true
(3)编译类型还是运行类型
AA aa = new BB()
aa编译类型AA,aa运行类型是BB
System.out.println(aa instanceof AA);
System.out.println(aa instanceof BB);
:true
:true