方法收到的是**原始變數的副本**。對副本重新賦值 (指向新物件) **不影響** 原變數;但透過副本**修改物件內部狀態** 則**影響共享物件**。
呼叫端:
Dog d = new Dog("Max"); // d ──► [Dog@100: name="Max"]
呼叫方法: void rename(Dog dog) { ... }
方法執行時:
參數 dog (副本) ──► [Dog@100: name="Max"] ◄── 原始 d
同一物件,兩個參考變數指向它
void swapRef(Dog dog) {
dog = new Dog("Buddy"); // 參數 dog 指向新物件
// 原始 d 仍指向 "Max"
}
**結果**:呼叫端 `d` 不變。參數 `dog` 是區域變數,重新賦值只改變區域副本指向。
void changeName(Dog dog) {
dog.setName("Buddy"); // 透過副本修改共享物件
}
**結果**:呼叫端 `d.getName()` 變為 `"Buddy"`。兩個參考指向**同一物件**,修改內容當然可見。
void modifyString(String s) {
s = s + " World"; // 字串不可變,實際產生新 String 物件
// 參數 s 指向新物件,原始字串不變
}
**結果**:呼叫端字串不變。`String`、`Integer`、`BigDecimal` 等不可變類別,「修改」操作實為**建立新物件並讓區域參考指向它**,原物件不變。
class Dog { String name; Dog(String n){name=n;} void setName(String n){name=n;} }
public class PassByValueDemo {
public static void main(String[] args) {
// 1. 基本型別
int x = 10;
modifyPrimitive(x);
System.out.println("基本型別: " + x); // 10
// 2. 可變物件 - 修改內部狀態
Dog d1 = new Dog("Max");
modifyDogState(d1);
System.out.println("修改狀態: " + d1.name); // Buddy
// 3. 可變物件 - 嘗試換引用
Dog d2 = new Dog("Max");
swapDogRef(d2);
System.out.println("換引用: " + d2.name); // Max (不變)
// 4. 不可變物件
String s = "Hello";
modifyString(s);
System.out.println("字串: " + s); // Hello (不變)
}
static void modifyPrimitive(int n) { n = 20; }
static void modifyDogState(Dog d) { d.setName("Buddy"); }
static void swapDogRef(Dog d) { d = new Dog("Buddy"); }
static void modifyString(String s) { s = s + " World"; }
}
public void setBirthDate(Date d) { this.birthDate = new Date(d.getTime()); }
public Date getBirthDate() { return new Date(birthDate.getTime()); }
Java **永遠傳值**。理解「參考值副本」與「共享物件」的區別,配合防御性複製、不可變設計、明確的 API 契約 (輸入參數只讀、輸出用返回值),即可徹底避開參數傳遞陷阱,寫出可靠、可推理的代碼。