多态
方法和对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
例子引出多态
假设有一个主人Master类,其中有一个feed(喂食)方法,可以完成主人给动物喂食的信息。假设Food类有两个子类:Fish和Bone,动物Animal类有两个子类Cat和Dog。根据前面知识可以写出代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.learn.poly_;
public 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; } }
|
1 2 3 4 5 6 7 8
| package com.learn.poly_;
public class Dog extends Animal{ public Dog(String name) { super(name); } }
|
1 2 3 4 5 6 7 8
| package com.learn.poly_;
public class Bone extends Food{ public Bone(String name) { super(name); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.learn.poly_;
public class Food { private String name;
public Food(String name) { this.name = name; }
public String getName() { return name; }
public void setName(String name) { this.name = name; } }
|
1 2 3 4 5 6 7 8
| package com.learn.poly_;
public class Fish extends Food{ public Fish(String name) { super(name); } }
|
1 2 3 4 5 6 7 8
| package com.learn.poly_;
public class Bone extends Food{ public Bone(String name) { super(name); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.learn.poly_;
public class Master { private String name;
public Master(String name) { this.name = name; }
public void feed(Dog dog,Bone bone){ System.out.println("主人 "+name+"给 "+dog.getName()+"吃 "+bone.getName()); }
public void feed(Cat cat,Fish fish){ System.out.println("主人 "+name+"给 "+cat.getName()+"吃 "+fish.getName()); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.learn.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);
Cat cat = new Cat("汤姆"); Fish fish = new Fish("小鱼"); tom.feed(cat,fish);
} }
|
可以看到feed方法通过重载的方式来实现对不同类别动物的喂食操作,这样如果动物、食物种类很多,那是不是意味着要写很多种不同的方法来重载呢?
这个问题可以通过对象的多态性来解决。
多态基本介绍
方法的多态:重写和重载 。
对象的多态(核心):
1 2 3 4
| Animal animal = new Dog();
|
- 2、编译类型在定义对象时,就确定了,不能改变
- 3、运行类型是可以变化的
- 4、编译类型看定义时=号的左边,运行类型看=号的右边。
了解了方法和对象的多态性之后,可以对上面例子的代码进行简化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.learn.poly_;
public class Master { private String name;
public Master(String name) { this.name = name; }
public void feed(Animal animal,Food food){ System.out.println("主人 "+name+"给 "+animal.getName()+" 吃 "+food.getName()); }
}
|
使用多态机制后,就算想要添加新的类,也不需要重写Master类,只需要添加新的类别,在主函数中new对象即可,大大提高了写代码的效率,以及代码的复用性。
多态的细节和注意事项
多态的前提是:两个对象(态)是存在继承关系
多态的向上转型:
- 1、本质:父类的引用指向了子类的对象
- 2、语法:父类类型 引用名 = new 子类类型();
- 3、特点:编译类型看左边,运行类型看右边。遵守的规则:
- (1)可以调用父类中的所有成员(需遵守访问权限)
- (2)不能调用子类中的特有成员(因为在编译阶段,能调用哪些成员是由编译类型来决定的);
- (3)最终运行效果看子类(运行类型)的具体实现(即调用方法时,按照从子类开始查找方法,然后去调用,规则和前面讲的方法调用规则一致)。
多态的向下转型:
- 1、语法:子类类型 引用名 = (子类类型)父类引用
1 2 3 4 5 6 7
| Animal animal = new Cat();
Cat cat = (Cat) animal; cat.privateMethod();
|
- 2、只能强转父类的引用,不能强转父类的对象
- 3、要求父类的引用必须指向的是当前目标类型的对象
1 2 3
| Animal animal = new Cat(); Cat cat = (Cat) animal; Dog dog = (Dog) animal;
|
属性没有重写之说!属性的值看编译类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class PolyDetail02{ public static void main(String[] args){ Base base = new Sub(); System.out.println(base.count); } }
class Base{ int count = 10; }
class Sub extends Base{ int count = 20; }
|
- instanceOf比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型
动态绑定机制
java重要特性:动态绑定机制
- 1、当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定(因为运行类型是在堆中,有一个地址)。
- 2、当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class A{ public int i = 10; public int sum(){ return getI()+10; } public int sum1(){ return i+10; } public int getI(){ return i; } }
class B extends A{ public int i = 20; public int sum(){ return i+20; } public int sum1(){ return i+10; } public int getI(){ return i; } }
A a = new B(); System.out.println(a.sum()); System.out.println(a.sum1());
|
第二种情况:假设子类中的sum()被注释了,输出为何?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class A{ public int i = 10; public int sum(){ return getI()+10; } public int sum1(){ return i+10; } public int getI(){ return i; } }
class B extends A{ public int i = 20; public int sum1(){ return i+10; } public int getI(){ return i; } }
A a = new B(); System.out.println(a.sum());
System.out.println(a.sum1());
|
接着继续注销子类中的sum1()方法,结果又为何?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class A{ public int i = 10; public int sum(){ return getI()+10; } public int sum1(){ return i+10; } public int getI(){ return i; } }
class B extends A{ public int i = 20; public int getI(){ return i; } }
A a = new B(); System.out.println(a.sum());
System.out.println(a.sum1());
|
多态的应用
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class PloyArray { public static void main(String[] args) { Person[] persons = new Person[5]; persons[0] = new Person("jack",29); persons[1] = new Student("smith",19,100); persons[2] = new Student("Alan",18,60.0); persons[3] = new Teacher("scott",40,20000); persons[4] = new Teacher("kobe",43,250000);
for(int i = 0 ; i<persons.length;i++){ System.out.println(persons[i].say()); if(persons[i] instanceof Student){ ((Student) persons[i]).study(); }else if(persons[i] instanceof Teacher){ ((Teacher) persons[i]).teach(); }else if(persons[i] instanceof Person){
}else{ System.out.println("类型有误"); } } } }
|
多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型