方法
1.方法的作用
Java中方法(或函式)的作用是多方面的,它們是實現物件導向程式設計(OOP)核心概念的重要工具。以下是Java中方法的一些主要作用:
- 程式碼重用:
方法允許你將程式碼組織成可重用的單元。一旦你定義了一個方法,你就可以在程式的多個地方呼叫它,而無需重複編寫相同的程式碼。這不僅可以減少程式碼量,還可以提高程式碼的可維護性和可讀性。 - 模組化:
透過將程式分解為獨立的方法,你可以將複雜的任務分解成更小、更易於管理的部分。每個方法都負責完成一個特定的任務,這使得理解和修改程式變得更加容易。 - 封裝:
封裝是OOP的一個基本原則,它要求將資料和操作這些資料的方法組合在一起,形成一個類。方法作為類的成員,可以訪問和操作類的私有資料,而外部程式碼則只能透過公共方法(介面)與類進行互動。這有助於隱藏類的內部實現細節,並保護資料不被隨意修改。 - 提高可讀性:
透過給方法命名,你可以清楚地表達該方法的功能。這使得其他開發者(或未來的你)在閱讀程式碼時能夠更容易地理解每個部分的作用。 - 實現多型性:
在Java中,你可以透過方法重寫(Override)和方法過載(Overload)來實現多型性。方法重寫允許子類提供一個特定於子類的實現,而方法過載則允許在同一個類中定義多個同名但引數列表不同的方法。多型性使得你可以編寫更加靈活和可擴充套件的程式碼。 - 異常處理:
方法可以宣告它可能丟擲的異常,這使得呼叫者能夠知道在呼叫該方法時可能需要處理哪些型別的錯誤。透過丟擲異常,方法可以向呼叫者報告錯誤情況,並允許呼叫者採取適當的措施來解決問題。 - 促進程式碼的組織和結構化:
透過將程式碼組織成方法,你可以更好地控制程式的執行流程。你可以按照邏輯順序呼叫不同的方法,以完成複雜的任務。此外,透過將相關的方法組織在同一個類中,你可以進一步促進程式碼的結構化和模組化。 - 實現遞迴:
遞迴是一種強大的程式設計技術,它允許方法呼叫自身來解決問題。遞迴方法特別適用於解決那些可以分解為更小相似問題的任務,如遍歷樹形結構、計算階乘等。
2.方法的定義
在Java中,方法的定義是建立一個可執行的程式碼塊,該程式碼塊封裝了實現特定功能所需的所有語句。方法(也稱為函式)是Java程式的基本構建塊之一,它們允許你將程式碼組織成可重用的單元,從而提高程式碼的可讀性和可維護性。
一個完整的方法定義包括以下幾個部分:
- 訪問修飾符(Access Modifier):定義了方法的訪問級別,即誰可以呼叫這個方法。Java中有四種訪問修飾符:
public
、protected
、private
(以及預設修飾符,即不顯式指定訪問修飾符的情況)。 - 返回型別(Return Type):方法執行完畢後返回的資料型別。如果方法不返回任何值,則返回型別應為
void
。 - 方法名(Method Name):方法的唯一識別符號,它遵循Java的命名約定(如駝峰命名法)。
- 引數列表(Parameter List):方法接收的輸入值,它是一個用逗號分隔的引數宣告列表。每個引數宣告包括引數型別和引數名。如果方法不接受任何引數,則引數列表為空。
- 方法體(Method Body):包含實現方法功能的Java語句的集合。方法體用大括號
{}
包圍。 - 返回語句(Return Statement,對於非
void
方法):可選的,用於結束方法的執行並將控制權返回給方法呼叫者。它還可以返回一個值給呼叫者。如果方法具有返回型別void
,則不需要(也不允許)包含返回語句。
下面是一個Java中方法定義的簡單示例:
public class Calculator {
// 定義一個計算兩個整數和的方法
public int add(int num1, int num2) {
// 方法體
int sum = num1 + num2;
// 返回語句
return sum;
}
// 定義一個不返回任何值的方法
public void displayMessage() {
// 方法體
System.out.println("Hello, Java!");
// 對於void方法,不需要返回語句
}
}
在這個例子中,Calculator
類包含兩個方法:add
和displayMessage
。add
方法接受兩個整數作為引數,並返回它們的和。它使用了一個返回語句來返回計算結果。displayMessage
方法不接受任何引數,也不返回任何值(其返回型別為void
),它僅列印一條訊息到控制檯。
注意,在Java中,方法定義是類的一部分,所以你需要將它們放在類定義內部。此外,雖然上面的示例展示了在類內部定義靜態方法之前的類定義,但你也可以在介面中定義抽象方法(這些方法沒有方法體),並在實現介面的類中提供具體實現。
3.方法的實參和形參
在Java中,方法呼叫時涉及的兩個關鍵概念是形參(形式引數)和實參(實際引數)。這兩個概念在方法定義和方法呼叫時起著重要的作用,它們之間的關係是理解Java方法呼叫機制的基礎。
3.1.形參(Formal Parameters)
形參是定義在方法簽名中的變數,用於在方法體內接收呼叫者傳遞的資料。在方法被呼叫之前,形參不佔用記憶體中的儲存單元。形參只在方法被呼叫時才分配記憶體單元,其記憶體單元的大小和型別由呼叫時傳入的實參決定。在方法執行結束後,形參所佔用的記憶體單元會被釋放。
形參的命名和作用域僅限於定義它的方法內部。在方法體內,你可以透過形參來訪問呼叫者傳遞給方法的資料。
3.2.實參(Actual Parameters)
實參是在呼叫方法時傳遞給方法的實際值。實參可以是常量、變數、表示式或另一個方法的返回值。在方法呼叫時,實參的值被傳遞給對應的形參,以便在方法體內使用。
需要注意的是,實參在方法呼叫之前就已經被計算並確定了其值(對於表示式和另一個方法的返回值作為實參的情況),而形參的值是在方法呼叫時由實參賦予的。
3.3.形參與實參的傳遞
在Java中,無論是基本資料型別還是引用資料型別,方法呼叫時傳遞的都是值。但是,這裡的“值”的含義對於基本資料型別和引用資料型別是不同的。
- 對於基本資料型別(如int、float、char等),傳遞的是實參的值的一個副本。因此,在方法體內對形參的修改不會影響到實參本身。
- 對於引用資料型別(如物件、陣列等),傳遞的是實參(即物件的引用或陣列名)的值的副本。這意味著方法體內和方法體外都指向同一個物件或陣列,因此在方法體內對物件或陣列所做的修改會影響到原始物件或陣列。但是,如果方法體內改變了形參引用所指向的物件(即重新為形參分配了一個新的物件),那麼這個改變不會影響到實參所引用的物件。
示例
public class Test {
public static void main(String[] args) {
int x = 10;
System.out.println("呼叫前x的值:" + x);
modify(x);
System.out.println("呼叫後x的值:" + x); // 輸出10,因為x是基本資料型別,傳遞的是值的副本
MyClass obj = new MyClass();
obj.value = 100;
System.out.println("呼叫前obj.value的值:" + obj.value);
modifyObject(obj);
System.out.println("呼叫後obj.value的值:" + obj.value); // 輸出101,因為obj是引用資料型別,傳遞的是引用的副本,但指向同一個物件
}
public static void modify(int num) {
num = 20; // 修改的是num(形參)的值,對x(實參)沒有影響
}
public static void modifyObject(MyClass obj) {
obj.value = 101; // 修改的是obj(形參)所引用的物件的屬性,對原始物件也有影響
// 如果這裡寫 obj = new MyClass(); 那麼原始物件就不會被修改
}
}
class MyClass {
int value;
}
4.JVM記憶體結構劃分
JVM(Java虛擬機器)的記憶體結構主要可以劃分為以下幾個部分:堆(Heap)、方法區(Method Area)、虛擬機器棧(VM Stack)、程式計數器(Program Counter Register)以及本地方法棧(Native Method Stack)。以下是這些部分的詳細劃分和說明:
1. 堆(Heap)
- 定義與特點:堆是JVM中最大的一塊記憶體區域,用於存放物件例項和陣列。堆被所有執行緒共享,是垃圾收集器管理的主要區域,因此也被稱為“GC堆”。
- 記憶體劃分:堆通常被細分為新生代(Young Generation)和老年代(Old Generation)。新生代又被進一步劃分為Eden區、From Survivor區(也稱為S0區)和To Survivor區(也稱為S1區),預設情況下這些區域的比例為8:1:1。
- 垃圾收集:堆記憶體是垃圾收集的主要區域,採用分代收集演算法,以提高垃圾回收的效率。
2. 方法區(Method Area)
- 定義與特點:方法區用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。它是執行緒共享的記憶體區域,在JDK 1.8之前被稱為永久代(Permanent Generation),但在JDK 1.8及以後,永久代被移除,取而代之的是元空間(Meta Space),元空間位於本地記憶體中。
- 記憶體回收:方法區記憶體回收的目標主要是常量池的回收和型別的解除安裝,但這部分回收相對較難實現。
3. 虛擬機器棧(VM Stack)
- 定義與特點:虛擬機器棧是每個執行緒私有的記憶體區域,用於儲存區域性變數表、運算元棧、動態連結、方法出口等資訊。每個方法被呼叫時,都會建立一個棧幀(Stack Frame)來儲存這些資訊,方法執行完成後,對應的棧幀會被銷燬。
- 異常處理:如果執行緒請求的棧深度超過最大值,會丟擲
StackOverflowError
異常;如果棧進行動態擴充套件時無法申請到足夠記憶體,會丟擲OutOfMemoryError
異常。
4. 程式計數器(Program Counter Register)
- 定義與特點:程式計數器是一塊較小的記憶體空間,用於記錄當前執行緒所執行的位元組碼的行號指示器。它是執行緒私有的,每條執行緒都有一個獨立的程式計數器,用於指示下一條要執行的位元組碼指令。
- 特殊性質:程式計數器是唯一一個在Java虛擬機器規範中沒有規定任何
OutOfMemoryError
情況的區域。
5. 本地方法棧(Native Method Stack)
- 定義與特點:本地方法棧與虛擬機器棧的作用非常相似,不過它主要為虛擬機器使用到的Native方法服務。Native方法一般是用其他語言(如C、C++或組合語言)編寫的,並且被編譯為基於本機硬體和作業系統的程式。
- 異常處理:與虛擬機器棧一樣,本地方法棧也會丟擲
StackOverflowError
和OutOfMemoryError
異常。
5.方法的過載
在Java中,方法的過載(Overloading)是一種允許在同一個類中定義多個同名方法但引數列表不同的特性。引數列表的不同可以體現在引數的個數、型別或順序上。過載的方法可以有不同的返回型別,但返回型別不能作為區分方法過載的依據。
方法過載的規則
- 方法名必須相同:過載的方法必須具有相同的名稱。
- 引數列表必須不同:這可以透過改變引數的型別、數量或者順序來實現。
- 返回型別可以不同:雖然返回型別在過載方法中可以是不同的,但它不是區分過載方法的因素。過載的確定發生在編譯時,而返回型別是在執行時才知道的。
- 訪問修飾符可以不同:過載的方法可以有不同的訪問修飾符(如public、protected、private)。
- 丟擲的異常可以不同:過載的方法可以宣告丟擲不同的異常,但這也不是過載的主要決定因素。
示例
下面是一個簡單的Java類,它展示了方法過載的概念:
public class OverloadExample {
// 方法1:列印一個整數
public void print(int i) {
System.out.println("Printing int: " + i);
}
// 方法2:列印一個雙精度浮點數
public void print(double f) {
System.out.println("Printing double: " + f);
}
// 方法3:列印一個字串
public void print(String s) {
System.out.println("Printing string: " + s);
}
// 方法4:列印兩個整數
public void print(int i, int j) {
System.out.println("(" + i + ", " + j + ")");
}
public static void main(String[] args) {
OverloadExample obj = new OverloadExample();
obj.print(5); // 呼叫 print(int)
obj.print(5.0); // 呼叫 print(double)
obj.print("Hello"); // 呼叫 print(String)
obj.print(10, 20); // 呼叫 print(int, int)
}
}
在上面的例子中,print
方法被過載了四次,每次的引數列表都不同。在main
方法中,透過傳遞不同型別的引數,可以呼叫到不同版本的print
方法。
注意事項
- 僅僅返回型別不同,不足以成為方法過載的依據。
- 僅僅訪問修飾符或丟擲的異常不同,也不是過載的依據。
- 引數列表的不同是過載方法的主要區分點。