一、多态polymorphic
1、泛化
泛化就是抽象化,把具体的事物当作抽象的东西看待(把猫看成动物)。
泛化也叫向上转型,是通往多态的路口。
把子类对象看作父类对象,这样可行吗?
可行,自愿放弃了更丰富的信息。
这样做有用吗?
有用,我们可以用同样的类型处理不同的东西。
示例代码
public class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
public void eat(){
System.out.println("吃");
}
public void sleep(){
System.out.println("睡");
}
}
public class Cat extends Animal {
private String eyeColor;
public String getEyeColor() {
return eyeColor;
}
public void setEyeColor(String eyeColor) {
this.eyeColor = eyeColor;
}
public void climbTree(){
System.out.println("爬树###");
}
}
public class Demo01_泛化 {
/*泛化——向上转型
*子类中特有的内容将不能被访问
* */
public static void main(String[] args) {
/*
Cat cat = new Cat();
cat.climbTree();
cat.eat();
cat.sleep();
Animal a = cat; // 把猫看成动物——泛化——向上转型
a.eat();
a.sleep();
//a.climbTree(); //子类中特有的内容不能访问
*/
//Animal a = new Cat(); //泛化——父类的引用指向子类对象
/*
Animal a = new Animal();
Cat cat= (Cat)a; // 向下转型——强转 很不安全
cat.climbTree();
*/
/*
Animal a = new Cat();
Cat c = (Cat)a; // 这样做还勉强可以
*/
}
}
这样做的缺点就是Cat中特有的方法就不能调用了。
注意:只有向上转型,没有向下转型。向下转型叫做强转,这样做很不安全!
Cat c = (Cat)o; 编译可以通过,但是运行时会出现错误。
注意:泛化指的是引用的泛化,不是对象的泛化,内存中的对象还是原来的对象,只是把引用的类型给改变了。
2、多态
(1)多态的概念
多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
简单来说,多态就是一个事物的多种状态(形态)。
现实生活中,关于多态的例子不胜枚举。比方说按下 F1 键这个动作,如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;如果当前在 Word 下弹出的就是Word 帮助;在 Windows 下弹出的就是 Windows 帮助和支持。同一个事件发生在不同的对象上会产生不同的结果。
(2)多态的分类
Java中的多态,可以分为静态多态和动态多态。
静态多态就是指方法的重载。
动态多态是指“动态绑定”。而我们这里所说的多态,大多数是指动态多态。
(3)多态应该具备的条件
1 继承
2 子类重写父类的方法
3 父类引用指向子类对象(泛化)
(4)动态绑定
所谓动态绑定,是指在编译期不能确定引用指向什么类型,而是在运行期选择具体的类型。
例如:
Animal a = new Cat() 编译期不能确定类型。
运行时确定是Cat类型。
为什么说编译器不能确定是Cat类型呢?
public class Animal {
String name="动物";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void eat(){
System.out.println("吃");
}
public void sleep(){
System.out.println("睡");
}
public static void sayHello(){
System.out.println("你好,我是动物");
}
}
public class Cat extends Animal {
String name="猫";
//重写
public void eat(){
System.out.println("吃鱼@@@");
}
//重写
public void sleep(){
System.out.println("白天睡觉,晚上行动@@@");
}
public void climbTree(){
System.out.println("爬树");
}
public static void sayHello(){
System.out.println("你好,我是猫");
}
}
public class Dog extends Animal {
//重写
public void eat(){
System.out.println("吃骨头...");
}
//重写
public void sleep(){
System.out.println("在狗窝中睡觉...");
}
}
public class Demo01_动态绑定_多态 {
double ran = Math.random();
Animal a;
if(ran>0.5){
a = new Cat();
}else{
a = new Dog();
}
a.eat();
}
}
编译器能确定a的类型吗??? 不能,因为编译期,随机数的值还没有生成。
子类重写父类的方法。
当使用父类的引用指向子类的对象时,调用这个重写的方法,调用到的是子类重写的方法。
示例代码
Animal a = new Cat();
//a.climbTree();
a.eat(); //动态绑定——绑定的是Cat类中重写的eat方法
总结
多态中成员方法的访问特点:编译看左边,运行看右边
(5)静态绑定
静态绑定是指编译期就确定调用的是父类中的成员。
动态绑定只是针对类中的普通方法,而对于成员变量,静态变量,静态方法,private修饰的方法,采用的是静态绑定。也就是说,对于成员变量,静态变量,和静态方法,绑定是声明时类型中的成员。
示例代码
Animal a = new Cat();
a.eat(); //动态绑定
System.out.println(a.name); //静态绑定
a.sayHello(); // 静态绑定
总结:成员变量、静态方法、静态变量、私有变量等采用静态绑定
编译看左边,运行看左边
(6)多态带来的好处
站在更高的层次管理对象的行为
软件的所谓蓝图化设计
例如:气象专家告诉农民收割庄稼
(7)多态的应用
案例1: 多态做为形参(接受范围更广的对象,避免方法重载过度使用)
需求:完成喂动物操作。定义一个动物园类,类中有一个喂动物的方法。
动物包括:老虎,猴子,狗,猫。每个动物类中都有一个eat的方法。(喂动物即是调用动物的eat方法)
public class Animal {
public void eat(){
System.out.println("吃");
}
}
public class Tiger extends Animal{
public void eat(){
System.out.println("吃肉###");
}
}
public class Monkey extends Animal{
public void eat(){
System.out.println("吃桃...");
}
}
public class Cat extends Animal{
public void eat(){
System.out.println("吃鱼");
}
}
public class Cat extends Animal{
public void eat(){
System.out.println("啃骨头");
}
}
public class Zoo {
public void feed(Animal a){
a.eat();
}
/*
public void feed(Tiger tiger){
tiger.eat();
}
public void feed(Monkey monkey){
monkey.eat();
}
public void feed(Cat cat){
cat.eat();
}
public void feed(Dog dog){
dog.eat();
}
*/
}
public static void main(String[] args) {
Zoo zoo = new Zoo();
Tiger tiger = new Tiger();
zoo.feed(tiger);
Monkey monkey = new Monkey();
zoo.feed(monkey);
Cat cat = new Cat();
zoo.feed(cat);
Dog dog = new Dog();
zoo.feed(dog);
}
案例2: 多态做为返回值类型(简单工厂模式)
需求:有一个生产汽车的工厂,有一个生产汽车的方法,根据客户不同的需求,可以生产出不同的汽车。汽车包括:奔驰(benz)、宝马(BMW)、法拉利
(ferrari)
public class Car {
public void run(){
System.out.println("run");
}
}
public class CarFactory {
public Car makeCar(String name){
if("bmw".equals(name)){
return new BMW();
}
if("benz".equals(name)){
return new Benz();
}
if("ferrari".equals(name)){
return new Ferrari();
}
return null;
}
/*
public BMW makeBMW(){
return new BMW();
}
public Benz makeBenz(){
return new Benz();
}
public Ferrari makeFerrari(){
return new Ferrari();
}
*/
}
public class BMW extends Car{
public void run(){
System.out.println("宝马在路上飞驰...");
}
}
public class Benz extends Car{
public void run(){
System.out.println("奔驰在路上奔驰###");
}
}
public class Ferrari extends Car{
public void run(){
System.out.println("法拉利跑的很快");
}
}