黑马程序员全套Java教程_Java基础教程_多态(十九)
1.1 多态概述
- 多态指同一个对象,在不同时刻表现出来的不同形态。以“猫”为例:
我们可以说猫是猫:猫 cat = new 猫();
也可以说猫是动物:动物 animal = new 猫();
这里猫在不同的时刻表现出来了不同的形态,这就是多态。 - 多态的前提和体现:
(1)有继承/实现关系
(2)有方法重写
(3)有父类引用指向子类对象
1.2 多态中成员访问特点
- 成员变量:编译看左边(想要使用的成员变量在父类中一定要有),执行看左边(使用的成员变量值为父类的值)。
成员方法:编译看左边(想要使用的成员方法在父类中一定要有),执行看右边(使用的成员方法如果在子类中有重写,使用子类的成员方法)。
成员变量和成员方法访问特点不一样的原因:成员方法有重写,而成员变量没有。 - 面向对象的特征之四——多态性
(1)多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单地说就是用同样的对象引用调用同样的方法但是做了不同的事情。
(2)多态性的分类:分为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外界提供的服务,那么运行时多态性可以解释为:当A系统访问B系统提供的服务时,B系统有多种提供服务的方式,但一切对A系统来说都是透明的(就像电动剃须刀是A系统,他的供电系统是B系统,B系统可以使用电池供电或者使用交流电,甚至还有可能是太阳能,A系统只会通过B类对象调用供电方法,但并不知道供电系统的底层实现是什么,究竟通过何种方式获得了动力)。==方法重载(overload)实现的是编译时的多态性(也称前绑定),而方法重写(override)==实现的是运行时的多态性(也称后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事情:a方法重写(子类继承父类并重写父类中已有的抽象的方法);b对象构造(用父类引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。
1.3 多态的好处和弊端
- 多态的好处:提高了程序的扩展性。
具体体现:定义方法的时候,使用父类型作为参数,将来在使用的时候,使用具体的子类型参与操作(用子类类型的对象传递给以父类类型对象为参数的方法)。比如说(涉及到后面抽象类和接口的内容),我们有一个Animal类和其子类Cat类,有一个用于操作Animal对象的AnimalOperation类,使用AnimalOperation类时要传入Animal的对象,但若Animal为抽象类或者接口,我们不能直接实例化,此时我们可以使用多态实例化。 - 多态的弊端:不能使用子类的特有功能。
面试题:面向对象的特征有哪些方面?
- 抽象
(1)是将一类对象的共同特征总结出来构造类的过程;
(2)抽象的分类:数据抽象和行为抽象两方面;
(3)抽象关注的是:对象有哪些属性和行为,并不关注这些行为的细节是什么。 - 继承
(1)是从已有类得到继承信息(包括已有类相同的属性和相同的方法)创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。
(2)继承的作用:让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段(如果不能理解请阅读阎宏博士的《Java与模式》或《设计模式精解》中关于桥梁模式的部分)。另外,继承提高了代码的复用性(多个类相同的成员可以放到同一个类中)和可维护性(如果方法的代码需要修改,修改一处即可)。 - 封装
(1)通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。
(2)面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可以隐藏的东西,只向外界提供最简单的编程接口(可以想想普通洗衣机和全自动洗衣机的差别,明显全自动洗衣机封装更好因此操作起来更简单;我们现在使用的只能手机也是封装得足够好,因为几个按键就搞定了所有的事情)。 - 多态性
(1)多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单地说就是用同样的对象引用调用同样的方法但是做了不同的事情。
(2)多态性的分类:分为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外界提供的服务,那么运行时多态性可以解释为:当A系统访问B系统提供的服务时,B系统有多种提供服务的方式,但一切对A系统来说都是透明的(就像电动剃须刀是A系统,他的供电系统是B系统,B系统可以使用电池供电或者使用交流电,甚至还有可能是太阳能,A系统只会通过B类对象调用供电方法,但并不知道供电系统的底层实现是什么,究竟通过何种方式获得了动力)。==方法重载(overload)==实现的是编译时的多态性(也称前绑定),而方法重写(override)实现的是运行时的多态性(也称后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事情:a方法重写(子类继承父类并重写父类中已有的抽象的方法);b对象构造(用父类引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。
1.4 多态中的转型
- (1)向上转型:从子到父,父类引用指向子类对象。
(2)向下转型:从父到子,父类引用转为子类对象(使用强转)。
public class AnimalDemo {
public static void main(String[] args) {
//向上转型
Animal a = new Cat();
a.eat();
//向下转型
Cat c = (Cat) a;
c.eat();
c.playGame();
//向上转型
a = new Dog();
a.eat();
//向下转型
//ClassCastException类型转换异常
Cat cc = (Cat) a;
cc.eat();
cc.playGame();
}
}
1.5 多态转型内存解析
- 略
1.6 案例一:猫和狗
- 需求:请采用多态的思想实现猫和狗的案例,并在测试类中进行测试。
- 思路及实现:
(1)定义动物类(Animal)
成员变量:姓名,年龄
构造方法:无参、带参
成员方法:重写吃饭()
public class Animal {
private String name;
private int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public Animal() {
}
public void eat(){
System.out.println("动物吃饭");
}
........................................
}
(2)定义猫类(Cat),继承动物类
构造方法:无参、带参
成员方法:重写吃饭()
public class Cat extends Animal{
public Cat(String name, int age) {
super(name, age);
}
public Cat() {
}
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
(3)定义测试类(AnimalDemo),写测试代码
public class AnimalDemo {
public static void main(String[] args) {
Animal a = new Cat();
a.setName("Jerry");
a.setAge(1);
a.eat();
Animal b = new Cat("CatKing",2);
b.eat();
}
}