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

此行為稱為虛擬方法呼叫,這些方法稱為虛擬方法。覆蓋的方法在執行時被呼叫,無論在編譯時的原始碼中使用了什麼資料型別的引用。

廣告