多态性(Polymorphism)
多态性是一种物体采取多种形式的能力。 当父类引用用于引用子类对象时,OOP中最常见的多态性使用发生。
任何可以通过多个IS-A测试的Java对象都被认为是多态的。 在Java中,所有Java对象都是多态的,因为任何对象都会为自己的类型和类Object传递IS-A测试。
重要的是要知道访问对象的唯一可能方法是通过引用变量。 引用变量只能是一种类型。 声明后,无法更改引用变量的类型。
可以将引用变量重新分配给其他对象,前提是它未声明为final。 引用变量的类型将确定它可以在对象上调用的方法。
引用变量可以引用其声明类型的任何对象或其声明类型的任何子类型。 引用变量可以声明为类或接口类型。
例子 (Example)
我们来看一个例子。
public interface Vegetarian{}
public class Animal{}
public class Deer extends Animal implements Vegetarian{}
现在,Deer类被认为是多态的,因为它具有多重继承。 以下示例如下:
- A Deer IS-A Animal
- A Deer IS-A Vegetarian
- A Deer IS-A Deer
- A Deer IS-A Object
当我们将引用变量事实应用于Deer对象引用时,以下声明是合法的 -
例子 (Example)
Deer d = new Deer();
Animal a = d;
Vegetarian v = d;
Object o = d;
所有引用变量d,a,v,o都指向堆中的相同Deer对象。
虚拟方法
在本节中,我将向您展示Java中重写方法的行为如何允许您在设计类时利用多态性。
我们已经讨论了方法覆盖,其中子类可以覆盖其父级中的方法。 重写方法基本上隐藏在父类中,除非子类在重写方法中使用super关键字,否则不会调用该方法。
例子 (Example)
/* File name : 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("Constructing an Employee");
this.name = name;
this.address = address;
this.number = number;
}
public void mailCheck() {
System.out.println("Mailing a check to " + 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类如下 -
/* File name : 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("Within mailCheck of Salary class ");
System.out.println("Mailing check to " + 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("Computing salary pay for " + getName());
return salary/52;
}
}
现在,您仔细研究以下程序并尝试确定其输出 -
/* File name : VirtualDemo.java */
public class VirtualDemo {
public static void main(String [] args) {
Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
System.out.println("Call mailCheck using Salary reference --");
s.mailCheck();
System.out.println("\n Call mailCheck using Employee reference--");
e.mailCheck();
}
}
这将产生以下结果 -
输出 (Output)
Constructing an Employee
Constructing an Employee
Call mailCheck using Salary reference --
Within mailCheck of Salary class
Mailing check to Mohd Mohtashim with salary 3600.0
Call mailCheck using Employee reference--
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.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()。
此行为称为虚方法调用,这些方法称为虚方法。 无论在编译时源代码中使用的引用是什么数据类型,都会在运行时调用重写的方法。