Java - 多态性
多态性是对象具有多种形式的能力。 OOP 中最常见的多态性使用发生在使用父类引用来引用子类对象时。
任何可以通过多个 IS-A 测试的 Java 对象都被认为是多态的。在 Java 中,所有 Java 对象都是多态的,因为任何对象都将通过其自身类型和 Object 类的 IS-A 测试。
重要的是要知道访问对象的唯一可能方法是通过引用变量。引用变量只能是一种类型。一旦声明,引用变量的类型就不能改变了。
引用变量可以重新分配给其他对象,前提是它没有声明为 final。引用变量的类型将决定它可以在对象上调用的方法。
引用变量可以引用其声明类型的任何对象或其声明类型的任何子类型。引用变量可以声明为类或接口类型。
示例
让我们看一个例子。
public interface Vegetarian{} public class Animal{} public class Deer extends Animal implements Vegetarian{}
现在,Deer 类被认为是多态的,因为它具有多重继承。对于上述示例,以下是正确的 -
- 鹿是动物
- 鹿是素食者
- 鹿就是鹿
- 鹿即是物
当我们将引用变量事实应用于 Deer 对象引用时,以下声明是合法的 -
示例
Deer d = new Deer(); Animal a = d; Vegetarian v = d; Object o = d;
所有的引用变量d、a、v、o都指向堆中同一个Deer对象。
虚拟方法
在本节中,我将向您展示 Java 中重写方法的行为如何让您在设计类时利用多态性。
我们已经讨论过方法覆盖,子类可以覆盖其父类中的方法。被覆盖的方法本质上隐藏在父类中,除非子类在覆盖方法中使用 super 关键字,否则不会被调用。
示例
/* 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(); } }
这将产生以下结果 -
输出
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 对象。一个使用薪水参考s , 另一个使用 Employee 引用 e .
在调用 s.mailCheck() 时 ,编译器在编译时看到 Salary 类中的 mailCheck(),JVM 在运行时调用 Salary 类中的 mailCheck()。
e 上的 mailCheck() 完全不同,因为 e 是员工参考。当编译器看到 e.mailCheck() ,编译器会看到 Employee 类中的 mailCheck() 方法。
这里,在编译时,编译器使用 Employee 中的 mailCheck() 来验证这个语句。然而,在运行时,JVM 会调用 Salary 类中的 mailCheck()。
这种行为称为虚方法调用,这些方法称为虚方法。无论在编译时源代码中使用的引用是什么数据类型,都会在运行时调用被覆盖的方法。
java