目录
6、IDEA查看构造器和方法形参列表快捷键:Ctrl + P
6.3 多态(Polymorphism)
多态是继封装、继承之后,面向对象的第三大特性。它表示一个对象具有多重特征,可以在特定的情况下表现出不同的状态。
6.3.1 多态解决什么样的问题
有的时候,我们在设计一个数组、或一个成员变量、或一个方法的形参、返回值类型时,无法确定它具体的类型,只能确定它是某个系列的类型。
案例:
(1)声明一个Dog类,包含public void eat()方法,输出“狗狗啃骨头”
(2)声明一个Cat类,包含public void eat()方法,输出“猫咪吃鱼仔”
(3)声明一个Person类,
-
包含宠物属性
-
包含领养宠物方法 public void adopt(宠物类型 pet)
-
包含喂宠物吃东西的方法 public void feed(),实现为调用宠物对象.eat()方法
问题: 1、从养狗切换到养猫怎么办? 修改代码把Dog修改为养猫? 2、或者有的人养狗,有的人养猫怎么办? 3、要是同时养多个狗,或猫怎么办? 4、要是还有更多其他宠物类型怎么办?
public class Dog {
public void eat(){
System.out.println("狗狗啃骨头");
}
}
public class Cat {
public void eat(){
System.out.println("猫咪吃鱼仔");
}
}
public class Person {
private Dog dog;
//adopt:领养
public void adopt(Dog dog){
this.dog = dog;
}
//feed:喂食
public void feed(){
if(dog != null){
dog.eat();
}
}
/*
问题:
1、从养狗切换到养猫怎么办?
修改代码把Dog修改为养猫?
2、或者有的人养狗,有的人养猫怎么办?
3、要是同时养多个狗,或猫怎么办?
4、要是还有更多其他宠物类型怎么办?
如果Java不支持多态,那么上面的问题将会非常麻烦,代码维护起来很难,扩展性很差。
*/
}
6.3.2 多态的形式和体现
1、多态引用
Java规定父类类型的变量可以接收子类类型的对象,这一点从逻辑上也是说得通的。
父类类型:指子类继承的父类类型。
所以说继承是多态的前提
2、多态引用的表现
表现:编译时类型与运行时类型不一致,编译时看“父类”,运行时看“子类”。
3、多态引用的好处和弊端
弊端:编译时,只能调用父类声明的方法,不能调用子类扩展的方法;
好处:运行时,看“子类”,如果子类重写了方法,一定是执行子类重写的方法体;变量引用的子类对象不同,执行的方法就不同,实现动态绑定。代码编写更灵活、功能更强大,可维护性和扩展性更好了。
4、多态演示
让Dog和Cat都继承Pet宠物类。
public class Pet {
private String nickname;
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public void eat(){
System.out.println(nickname + "吃东西");
}
}
public class Cat extends Pet {
//子类重写父类的方法
@Override
public void eat() {
System.out.println("猫咪" + getNickname() + "吃鱼仔");
}
//子类扩展的方法
public void catchMouse() {
System.out.println("抓老鼠");
}
}
public class Dog extends Pet {
//子类重写父类的方法
@Override
public void eat() {
System.out.println("狗狗" + getNickname() + "啃骨头");
}
//子类扩展的方法
public void watchHouse() {
System.out.println("看家");
}
}
public class TestPet {
public static void main(String[] args) {
//多态引用
Pet pet = new Dog();
pet.setNickname("小白");
//多态的表现形式
/*
编译时看父类:只能调用父类声明的方法,不能调用子类扩展的方法;
运行时,看“子类”,如果子类重写了方法,一定是执行子类重写的方法体;
*/
pet.eat();//运行时执行子类Dog重写的方法
// pet.watchHouse();//不能调用Dog子类扩展的方法
pet = new Cat();
pet.setNickname("雪球");
pet.eat();//运行时执行子类Cat重写的方法
}
}
6.3.3 应用多态解决问题
1、声明变量是父类类型,变量赋值子类对象
-
方法的形参是父类类型,调用方法的实参是子类对象
-
实例变量声明父类类型,实际存储的是子类对象
public class OnePersonOnePet {
private Pet pet;
public void adopt(Pet pet) {//形参是父类类型,实参是子类对象
this.pet = pet;
}
public void feed(){
pet.eat();//pet实际引用的对象类型不同,执行的eat方法也不同
}
}
public class TestOnePersonOnePet {
public static void main(String[] args) {
OnePersonOnePet person = new OnePersonOnePet();
Dog dog = new Dog();
dog.setNickname("小白");
person.adopt(dog);//实参是dog子类对象,形参是父类Pet类型
person.feed();
Cat cat = new Cat();
cat.setNickname("雪球");
person.adopt(cat);//实参是cat子类对象,形参是父类Pet类型
person.feed();
}
}
2、数组元素是父类类型,元素对象是子类对象
public class OnePersonManyPets {
private Pet[] pets;//数组元素类型是父类类型,元素存储的是子类对象
public void adopt(Pet[] pets) {
this.pets = pets;
}
public void feed() {
for (int i = 0; i < pets.length; i++) {
pets[i].eat();//pets[i]实际引用的对象类型不同,执行的eat方法也不同
}