Java 教程

Java 控制语句

面向对象编程

Java 内置类

Java 文件处理

Java 错误与异常

Java 多线程

Java 同步

Java 网络

Java 集合

Java 接口

Java 数据结构

Java 集合算法

高级 Java

Java 杂项

Java API 和框架

Java 类参考

Java 有用资源

Java - 多态



Java 中的多态

多态是指一个对象具有多种形态的能力。多态是 Java OOPs 概念 的重要特性,它允许我们使用单个方法名称(接口)执行多个操作。任何可以传递多个 IS-A 测试的 Java 对象都被认为是多态的。在 Java 中,所有 Java 对象 都是多态的,因为任何对象都会通过其自身类型和类 Object 的 IS-A 测试。

Java 中多态的用途

OOP 中多态最常见的用途是当父类引用用于引用子类对象时。

重要的是要知道,访问对象的唯一可能方法是通过 引用变量。引用变量只能有一种类型。一旦声明,引用变量的类型就不能更改。

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

引用变量可以引用其声明类型的任何对象或其声明类型的任何子类型。引用变量可以声明为类或接口类型。

Java 多态示例

让我们来看一个例子。

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

现在,Deer 类被认为是多态的,因为它具有多重继承。以下对上述示例成立:-

  • 一只鹿 IS-A 动物
  • 一只鹿 IS-A 素食动物
  • 一只鹿 IS-A 鹿
  • 一只鹿 IS-A 对象

当我们将引用变量事实应用于 Deer 对象引用时,以下声明是合法的:-

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

所有引用变量 d、a、v、o 都引用堆中的同一个 Deer 对象。

Java 多态实现

在此示例中,我们通过创建 Deer 的对象并将同一个对象分配给超类或已实现接口的引用来展示上述概念。

interface Vegetarian{}
class Animal{}
public class Deer extends Animal implements Vegetarian{
	public static void main(String[] args) {
		Deer d = new Deer();
		Animal a = d;
		Vegetarian v = d;
		Object o = d;
		
		System.out.println(d instanceof Deer);
		System.out.println(a instanceof Deer);
		System.out.println(v instanceof Deer);
		System.out.println(o instanceof Deer);
	}	
}

输出

true
true
true
true

Java 多态的类型

Java 中有两种类型的多态

  1. 编译时多态
  2. 运行时多态

Java 中的编译时多态

编译时多态也称为静态多态,它是通过 方法重载 实现的。

示例:编译时多态

此示例具有多个具有相同名称的方法来实现 Java 中编译时多态的概念。

// Java Example: Compile Time Polymorphism
public class Main {
  // method to add two integers
  public int addition(int x, int y) {
    return x + y;
  }

  // method to add three integers
  public int addition(int x, int y, int z) {
    return x + y + z;
  }

  // method to add two doubles
  public double addition(double x, double y) {
    return x + y;
  }

  // Main method
  public static void main(String[] args) {
    // Creating an object of the Main method
    Main number = new Main();

    // calling the overloaded methods
    int res1 = number.addition(444, 555);
    System.out.println("Addition of two integers: " + res1);

    int res2 = number.addition(333, 444, 555);
    System.out.println("Addition of three integers: " + res2);

    double res3 = number.addition(10.15, 20.22);
    System.out.println("Addition of two doubles: " + res3);
  }
}

输出

Addition of two integers: 999
Addition of three integers: 1332
Addition of two doubles: 30.369999999999997

Java 中的运行时多态

运行时多态也称为动态方法调度,它是通过 方法覆盖 实现的。

示例:运行时多态

// Java Example: Run Time Polymorphism
class Vehicle {
  public void displayInfo() {
    System.out.println("Some vehicles are there.");
  }
}

class Car extends Vehicle {
  // Method overriding
  @Override
  public void displayInfo() {
    System.out.println("I have a Car.");
  }
}

class Bike extends Vehicle {
  // Method overriding
  @Override
  public void displayInfo() {
    System.out.println("I have a Bike.");
  }
}

public class Main {
  public static void main(String[] args) {
    Vehicle v1 = new Car(); // Upcasting
    Vehicle v2 = new Bike(); // Upcasting

    // Calling the overridden displayInfo() method of Car class
    v1.displayInfo();

    // Calling the overridden displayInfo() method of Bike class
    v2.displayInfo();
  }
}

输出

I have a Car.
I have a Bike.

Java 中的虚拟方法和运行时多态

在本节中,我将向您展示 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();
   }
}

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;
   }
}

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;
   }
}

输出

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()。

此行为称为虚拟方法调用,这些方法称为虚拟方法。覆盖的方法在运行时被调用,无论在编译时的源代码中使用了什么数据类型的引用。

广告