概念

在编程语言和类型论中,多态指同一行为的多种表现形态。

向上转型

先梳理一下几个类与其继承关系,定义一个Animal父类,再分别定义两个子类Cat和Bird子类来继承Animal父类。

class Animal {
    }
}
class Cat extends Animal {
}

class Bird extends Animal {
}

1. 语法规则

父类 父类对象 = 子类对象;即 父类引用 引用了 子类对象
Animal animal = new Bird;

2. 形式

  • 直接赋值 Animal animal = new Bird;
  • 方法传参
public class Test { 
 public static void main(String[] args) { 
 Bird bird = new Bird("圆圆"); 
 feed(bird); 
 } 
 public static void feed(Animal animal) { 
 animal.eat("谷子"); 
 } 
}
  • 方法返回
public class Test { 
 public static void main(String[] args) { 
 Animal animal = findMyAnimal(); 
 } 
 public static Animal findMyAnimal() { 
 Bird bird = new Bird("圆圆"); 
 return bird; 
 } 
}

3. 动态绑定/运行时绑定

首先梳理一下父子类的关系:

class Animal {   //父类
    public void eat() {
        System.out.println("Animal");
    }
}
class Cat extends Animal {   //子类
    @Override
    public void eat() {
        System.out.println("Cat");
    }
}
class Bird extends Animal {  //子类
    public void fly() {
        System.out.println("正在飞!");
    }
    @Override
    public void eat() {
        System.out.println("Bird");
    }
}

Java定义父类 java定义animal父类_父类

  • 若子类中重写了父类定义的方法,那么父类的引用将调用子类的同名方法。
    子类Cat和Bird中都重写了父类定义的eat()方法,所以父类animal的引用调用的是子类中的eat()方法;
    Animal animal1= new Cat; Animal animal2= new Bird;animal1.eat(); --> Cat
    animal2.eat(); --> Bird
  • 编译时显示调用的是父类的方法,但在运行结果中显示调用的是子类的同名方法。
    下面我们可以简单的测试一下:
public static void main(String[] args) {
        Animal animal =  new Cat("花花");
        animal.eat();
    }

在Terminal命令框中cd进入相应目录下,javac -encoding utf-8 TestDemo.java编译java文件,javap -c TestDemo查看编译器为我们生成的字节码。

Java定义父类 java定义animal父类_父类_02


从上图的汇编代码中我们可以看到编译时调用的是父类的方法;但运行时的结果是Cat。

  • 在构造函数中,也会发生动态绑定。
class B {   //step1
 public B() {  //父类构造函数
 func(); 
 } 
 public void func() { 
 System.out.println("B.func()"); 
 } 
} 
class D extends B { 
 @Override   //step2
 public void func() {   //父类方法的重写
 System.out.println("D.func() "); 
 } 
} 
public class Test { 
 public static void main(String[] args) { 
 D d = new D(); 
 } 
} 
//D.func()

我们分析一下上述代码,在主函数中new了一个对象d,那就先需要执行D类的构造函数,(D类的构造函数缺省),而D类作为B类的子类,在构造时应该先帮助父类B类进行构造,于是先执行B类的构造函数,由于父类的构造函数中有被子类重写的普通方法,这时就会发生动态绑定,去执行D类的重写方法。

多态存在的必要条件

  • 继承关系
  • 方法重写
  • 父类引用指向子类对象,即向上转型

分类

重写式多态与重载式多态

优点

  1. 降低了类的使用成本
  2. 降低了圈复杂度,避免if-else的大量使用
  3. 可扩展能力强