面向对象-多态
什么是多态?
多态,即多种形态、多种状态。编译期一种形态,运行期一种形态。
多态核心代码:父类型引用指向子类型对象
public class Test {
public static void main(String[] args){
//父类型引用指向子类型对象
Animal cat = new Cat();
Animal dog = new Dog();
//调用eat()
cat.eat();
dog.eat();
}
}
class Animal {
public void eat(){
System.out.println("动物都能吃东西");
}
}
class Cat extends Animal {
//重写eat()
public void eat() {
System.out.println("猫吃猫粮");
}
}
class Dog extends Animal {
//重写eat()
public void eat() {
System.out.println("狗吃狗粮");
}
}
上述代码运行结果如下
对于上面的代码可以这样解释(Java程序的运行分为编译阶段和运行阶段)
- 在编译阶段,编译器只会检查语法,编译器检测到cat引用是Animal类型,编译器会到Animal.class字节码文件中去寻找eat(),找到后绑定Animal的eat()方法,完成编译阶段的绑定(静态绑定)
- 在运行阶段,通过引用会通过引用找到对象并检测到该对象是Cat类型,找到后绑定Cat对象的eat()方法,完成运行阶段的绑定(动态绑定)
向上转型和向下转型
转型条件:只有存在继承关系才可以转型
向上转型
子类型 —> 父类型
向上转型可以自动转换
向下转型
父类型 —> 子类型
向下转型需要加强制类型转换符
何时需要使用向下转型
在多态中,父类型引用子类型对象时,如果需要访问子类特有的属性或方法时,需要向下转型,否则编译无法通过。
public class Test {
public static void main(String[] args){
//父类型引用指向子类型对象(自动类型转换)
Animal cat = new Cat();
Animal dog = new Dog();
//子类特有方法catchMouse()
cat.catchMouse();
}
}
class Animal {
public void eat(){
System.out.println("动物都能吃东西");
}
}
class Cat extends Animal {
//重写eat()
public void eat() {
System.out.println("猫吃猫粮");
}
public void catchMouse() {
System.out.print("猫抓老鼠");
}
}
class Dog extends Animal {
//重写eat()
public void eat() {
System.out.println("狗吃狗粮");
}
}
在编译期,编译器会检测到cat引用时Animal类型,编译器会到Animal.class中寻找catchMouse(),而Animal.class中并没有catchMouse(),所以编译器报错。
向上转型可能会出现的异常
在多态中,父类型引用指向子类型对象,在运行阶段会检测引用实际指向的对象,如果向上转型转换类型出错,并不会在编译期出现问题,但在运行期会抛出类型转换异常(java.lang.ClassCastException)
public class Test {
public static void main(String[] args){
//父类型引用指向子类型对象
Animal dog = new Dog();
Cat d = (Cat)dog;
}
class Animal {
public void eat(){
System.out.println("动物都能吃东西");
}
}
class Cat extends Animal {
//重写eat()
public void eat() {
System.out.println("猫吃猫粮");
}
public void catchMouse() {
System.out.print("猫抓老鼠");
}
}
class Dog extends Animal {
//重写eat()
public void eat() {
System.out.println("狗吃狗粮");
}
}
在编译期,Animal类型的dog引用经过转型后变成Cat类型并将对象的地址给Cat类型的d引用,编译器检测到d是Cat类型所以编译能通过,但是在程序运行期间,检测到d引用指向的对象实际是Dog类型,所以会抛出类型转换异常(java.lang.ClassCastException)
如何避免ClassCastException
使用instanceof运算符判断引用指向对象的类型后再向下转型
instanceof运算符特点
- instanceof用于检测引用指向的对象数据类型
- instanceof运算结果是true/false
- instanceof语法格式:引用 instanceof 数据类型
具体代码如下
public class Test {
public static void main(String[] args){
Animal animal = new Cat();
if(animal instanceof Dog) {
Dog dog = (Dog)animal;
dog.eat();
}
if (animal instanceof Cat) {
Cat cat = (Cat)animal;
cat.eat();
}
}
}
class Animal {
public void eat() {
System.out.print("动物能进食");
}
}
class Dog extends Animal{
public void eat() {
System.out.print("狗吃狗粮");
}
}
class Cat extends Animal {
System.out.print("狗吃狗粮");
}
多态在开发中有什么作用?
降低程序耦合提高程序的扩展能力
我们以下面的一个多态的练习题为例
编写程序模拟主人喂养宠物
要求:主人有一个喂养方法feed(),可以达到喂养任何宠物的目的
public class Test {
public static void main(String[] args){
Dog dog = new Dog();
Cat cat = new Cat();
Master master = new Master();
master.feed(dog);
master.feed(cat);
}
}
class Pet {
public void eat(){}
}
class Dog extends Pet{
public void eat(){
System.out.println("狗吃狗粮");
}
}
class Cat extends Pet{
public void eat(){
System.out.println("猫吃猫粮");
}
}
class Master {
public void feed(Pet pet){
pet.eat();
}
}
对于上面的代码,public void feed(Pet)使Master和Dog以及Cat之间的耦合度降低,程序扩展力变强