位置:首页 > Java技术 > Java在线教程 > Java多态性

Java多态性

多态是对象采取多种形式表现的能力。当一个父类引用是用来指一个子类对象最常见多态性的用途是OOP中发生。

任何可以通过多个Java对象IS-A测试被认为是多态的。在Java中,所有的Java对象是多态的,因为任何对象将通过IS-A测试适合自己的类型和Object类。

重要的是要知道,访问一个对象的唯一可能途径是通过一个引用变量。引用变量只能是一个类型的。一旦声明,引用变量的类型不能改变。

引用变量可以重新分配,只要它没有声明为final的其他对象。参考变量的类型将决定,它可以在对象上调用的方法。

引用变量可以参考它的声明的类型或声明的类型的任何子类的任何对象。引用变量可以声明为一个类或接口类型。

例子:

让我们来看一个例子。

public interface Vegetarian{}
public class Animal{}
public class Deer extends Animal implements Vegetarian{}

现在,Deer类被认为是多态的,因为这有多重继承。以下是适用于上面的例子: 

  • 鹿IS-A 动物

  • 鹿IS-A 素食

  • 鹿 IS-A 鹿

  • 鹿 IS-A 对象

当我们应用的参考变量事实鹿对象引用,下面的声明是合法的:

Deer d = new Deer();
Animal a = d;
Vegetarian v = d;
Object o = d;

所有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("
 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

在这里,我们可实例化两种工资的对象。使用一个工资参考s,而使用其他的 Employee 引用e。

在调用 s.mailCheck() 时编译器看到 mailCheck() 在编译时Salary类,JVM 调用 mailCheck() 在 Salary类 在运行时。

关于e调用 mailCheck() 是完全不同的,因为 e 是一个 Employee 引用。当编译器看到 e.mailCheck(),编译器看到在 Employee 类中的 mailCheck() 方法。

在这里,在编译时,编译器使用mailCheck()员工,以验证这个语句。在运行时,但是,JVM调用 mailCheck() 在Salary类中。 

此行为被称为虚拟方法调用,并且该方法被称为虚拟方法。 Java中的所有方法的行为以这种方式,即一个重载方法被调用在运行时,无论什么数据类型的引用在源代码在编译时。