
Java 教程
- Java - 首頁
- Java - 概述
- Java - 歷史
- Java - 特性
- Java 與 C++
- JVM - Java 虛擬機器
- Java - JDK 與 JRE 與 JVM
- Java - Hello World 程式
- Java - 環境搭建
- Java - 基本語法
- Java - 變數型別
- Java - 資料型別
- Java - 型別轉換
- Java - Unicode 系統
- Java - 基本運算子
- Java - 註釋
- Java - 使用者輸入
- Java - 日期與時間
Java 控制語句
- Java - 迴圈控制
- Java - 決策制定
- Java - If-else
- Java - Switch
- Java - For 迴圈
- Java - For-Each 迴圈
- Java - While 迴圈
- Java - do-while 迴圈
- Java - Break
- Java - Continue
面向物件程式設計
- Java - OOPs 概念
- Java - 物件與類
- Java - 類屬性
- Java - 類方法
- Java - 方法
- Java - 變數作用域
- Java - 建構函式
- Java - 訪問修飾符
- Java - 繼承
- Java - 聚合
- Java - 多型
- Java - 重寫
- Java - 方法過載
- Java - 動態繫結
- Java - 靜態繫結
- Java - 例項初始化塊
- Java - 抽象
- Java - 封裝
- Java - 介面
- Java - 包
- Java - 內部類
- Java - 靜態類
- Java - 匿名類
- Java - 單例類
- Java - 包裝類
- Java - 列舉
- Java - 列舉建構函式
- Java - 列舉字串
Java 內建類
Java 檔案處理
Java 錯誤與異常
- Java - 異常
- Java - try-catch 塊
- Java - try-with-resources
- Java - 多重 catch 塊
- Java - 巢狀 try 塊
- Java - Finally 塊
- Java - throw 異常
- Java - 異常傳播
- Java - 內建異常
- Java - 自定義異常
Java 多執行緒
- Java - 多執行緒
- Java - 執行緒生命週期
- Java - 建立執行緒
- Java - 啟動執行緒
- Java - 執行緒連線
- Java - 執行緒命名
- Java - 執行緒排程器
- Java - 執行緒池
- Java - 主執行緒
- Java - 執行緒優先順序
- Java - 守護執行緒
- Java - 執行緒組
- Java - 關閉鉤子
Java 同步
Java 網路
- Java - 網路
- Java - Socket 程式設計
- Java - URL 處理
- Java - URL 類
- Java - URLConnection 類
- Java - HttpURLConnection 類
- Java - Socket 類
- Java - 泛型
Java 集合
Java 介面
Java 資料結構
Java 集合演算法
高階 Java
- Java - 命令列引數
- Java - Lambda 表示式
- Java - 傳送郵件
- Java - Applet 基礎
- Java - Javadoc 註釋
- Java - 自動裝箱和拆箱
- Java - 檔案不匹配方法
- Java - REPL (JShell)
- Java - 多版本 Jar 檔案
- Java - 私有介面方法
- Java - 內部類菱形運算子
- Java - 多解析度影像 API
- Java - 集合工廠方法
- Java - 模組系統
- Java - Nashorn JavaScript
- Java - Optional 類
- Java - 方法引用
- Java - 函式式介面
- Java - 預設方法
- Java - Base64 編碼解碼
- Java - Switch 表示式
- Java - Teeing 收集器
- Java - 微基準測試
- Java - 文字塊
- Java - 動態 CDS 檔案
- Java - Z 垃圾收集器 (ZGC)
- Java - 空指標異常
- Java - 打包工具
- Java - 密封類
- Java - 記錄類
- Java - 隱藏類
- Java - 模式匹配
- Java - 緊湊數字格式化
- Java - 垃圾回收
- Java - JIT 編譯器
Java 雜項
- Java - 遞迴
- Java - 正則表示式
- Java - 序列化
- Java - 字串
- Java - 程序 API 改進
- Java - 流 API 改進
- Java - 增強的 @Deprecated 註解
- Java - CompletableFuture API 改進
- Java - 流
- Java - 日期時間 Api
- Java 8 - 新特性
- Java 9 - 新特性
- Java 10 - 新特性
- Java 11 - 新特性
- Java 12 - 新特性
- Java 13 - 新特性
- Java 14 - 新特性
- Java 15 - 新特性
- Java 16 - 新特性
Java API 和框架
Java 類參考
- Java - Scanner
- Java - 陣列
- Java - 字串
- Java - Date
- Java - ArrayList
- Java - Vector
- Java - Stack
- Java - PriorityQueue
- Java - LinkedList
- Java - ArrayDeque
- Java - HashMap
- Java - LinkedHashMap
- Java - WeakHashMap
- Java - EnumMap
- Java - TreeMap
- Java - IdentityHashMap
- Java - HashSet
- Java - EnumSet
- Java - LinkedHashSet
- Java - TreeSet
- Java - BitSet
- Java - Dictionary
- Java - Hashtable
- Java - Properties
- Java - Collection
- Java - Array
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 中有兩種型別的多型
- 編譯時多型
- 執行時多型
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()。
此行為稱為虛擬方法呼叫,這些方法稱為虛擬方法。覆蓋的方法在執行時被呼叫,無論在編譯時的原始碼中使用了什麼資料型別的引用。