多态性是一种对象采取多种形式的能力。 当父类引用用于引用子类对象时,通过就会使用OOP中的多态性。
任何通过多个IS-A测试的Java对象都被认为是多态的。 在Java中,所有Java对象都是多态的,因为任何对象都会为自己的类型和类对象传递IS-A测试。
重要的是要知道访问对象的唯一方法是通过引用变量。 引用变量只能是一种类型。 声明后,无法更改引用变量的类型。
如果未将引用变量声明为final
,则可以将引用变量重新分配给其他对象。 引用变量的类型将确定它可以在对象上调用的方法。
引用变量可以引用其声明类型的任何对象或声明类型的任何子类型。 引用变量可以声明为类或接口类型。
示例
public interface Vegetarian{}
public class Animal{}
public class Deer extends Animal implements Vegetarian{}
现在,可将Deer
类认为是多态的,因为它具有多重继承。示例说明如下:
- Deer IS-A Animal
- Deer IS-A Vegetarian
- Deer IS-A Deer
- Deer IS-A Object
当将引用变量应用于Deer
对象引用时,以下声明是合法的 -
Deer d = new Deer();
Animal a = d;
Vegetarian v = d;
Object o = d;
所有引用变量:d
,a
,v
,o
都指向堆中的同一个Deer
对象。
虚拟方法
在本节中,将演示如何在Java中重写方法的行为,以在设计类时利用多态性。
前面章节中已经学习了方法覆盖,子类可以覆盖父级中的方法。重写方法基本上隐藏在父类中,除非子类在重写方法中使用super
关键字,否则不会调用父类方法。
示例
// 文件: Employee.java
public class Employee {
private String name;
private String address;
private int number;
public Employee(String name, String address, int number) {
System.out.println("构造一个:Employee");
this.name = name;
this.address = address;
this.number = number;
}
public void mailCheck() {
System.out.println("发送邮件检查:" + this.name + " " + this.address);
}
public String toString() {
return name + " " + address + " " + number;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public void setAddress(String newAddress) {
address = newAddress;
}
public int getNumber() {
return number;
}
}
现在扩展Employee
类如下 -
// 文件 : Salary.java
public class Salary extends Employee {
private double salary; // Annual salary
public Salary(String name, String address, int number, double salary) {
super(name, address, number);
setSalary(salary);
}
public void mailCheck() {
System.out.println("在Salary类中的 mailCheck()方法");
System.out.println("发送邮件检查:" + getName() + " with salary " + salary);
}
public double getSalary() {
return salary;
}
public void setSalary(double newSalary) {
if (newSalary >= 0.0) {
salary = newSalary;
}
}
public double computePay() {
System.out.println("计算应付工资:" + getName());
return salary / 52;
}
}
仔细研究下面的程序并尝试判断它的输出结果 -
// 文件:VirtualDemo.java
public class VirtualDemo {
public static void main(String[] args) {
Salary s = new Salary("Maxsu", "Renmin Road No.688 Haikou", 3, 9600.00);
Employee e = new Salary("董小姐", "Daxing Road No.188 Haikou", 2, 8400.00);
System.out.println("调用 Salary 类引用的 mailCheck()方法");
s.mailCheck();
System.out.println("调用 Employee 类引用的 mailCheck()方法");
e.mailCheck();
}
}
执行上面示例代码,得到以下结果 -
构造一个:Employee
构造一个:Employee
调用 Salary 类引用的 mailCheck()方法
在Salary类中的 mailCheck()方法
发送邮件检查:Maxsu with salary 9600.0
调用 Employee 类引用的 mailCheck()方法
在Salary类中的 mailCheck()方法
发送邮件检查:董小姐 with salary 8400.0
在上面代码中,实例化两个Salary
对象。一个使用Salary
类的引用s
,另一个使用Employee
类引用e
。
在调用s.mailCheck()
时,编译器在编译时看到Salary
类中有mailCheck()
方法,并且JVM在运行时调用Salary
类中的mailCheck()
方法。
e
实例变量的mailCheck()
是完全不同的,因为e
是一个Employee
引用。 当编译器看到e.mailCheck()
时,编译器会在Employee
类中找到mailCheck()
方法。
在编译时,编译器使用Employee
中的mailCheck()
执行验证。但是,在运行时,JVM调用Salary
类中的mailCheck()
方法。
此行为称为虚方法调用,这些方法称为虚方法。 无论在编译时源代码中使用的引用是什么数据类型,都会在运行时调用重写方法。