public class StringPassByValue {
public static void change(String x) {
x = "cd"; // 試圖修改引用指向
}
public static void main(String[] args) {
String x = new String("ab");
change(x);
System.out.println(x); // 輸出什麼? "ab" 還是 "cd"?
}
}
輸出結果: ab
「x 存儲堆中 "ab" 對象的引用,傳入 change() 時仍指向 "ab"。Java 是值傳遞,傳的是引用值。方法內創建 "cd" 對象,x 指向 "cd",所以原來的 x 變了。」
錯誤點:混淆了「引用的值傳遞」與「引用傳遞」。圖解中聲稱「x 指向 cd 對象」暗示原始變量被修改,實際上修改的是參數副本。
x 存儲對象 "ab" 的引用(內存地址,假設 0x1000)0x1000)傳給參數 x(新棧幀中的局部變量)x 現在持有 0x1000,指向 "ab""cd" 對象(假設 0x2000),將參數 x 的值改為 0x2000"ab"
main() 棧幀 change() 棧幀(調用期間)
┌─────────────┐ ┌─────────────┐
│ x ──────────┼──────────────►│ 0x1000 │ ← 初始:同樣指向 "ab"
└─────────────┘ └─────────────┘
執行 x = "cd" 後:
┌─────────────┐
│ x ──────────┼──► 0x2000 ("cd")
└─────────────┘
main 的 x 不受影響!
Java 中所有參數傳遞都是「按值傳遞」(Call by Value)。
—— Java Language Specification, Chapter 8.4.1
int a = 5 → 副本也是 5)// C++ 引用傳遞:參數是實參的別名
void change(string &x) { x = "cd"; } // 直接修改實參對象
int main() {
string x = "ab";
change(x);
cout << x; // 輸出 "cd" —— 真正的引用傳遞
}
既然不能換引用,那就修改對象內部狀態(前提:對象可變)。
public class MutableDemo {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("ab");
modify(sb);
System.out.println(sb); // 輸出 "abcd" —— 外部對象被修改!
}
public static void modify(StringBuilder x) {
x.append("cd"); // 調用方法修改內部字符數組,未改變 x 引用
// x = new StringBuilder("xx"); // 這行會切斷與外部的聯繫!
}
}
| 題目 | 答案 | 核心考點 |
|---|---|---|
| Java 是值傳遞還是引用傳遞? | 值傳遞(引用類型傳的是引用的值) | 參數傳遞機制 |
| 方法內修改參數引用,外部變嗎? | 不變(修改的是副本) | 棧幀隔離 |
| 方法內調用參數對象方法修改字段,外部變嗎? | 變(同一對象,共享堆內存) | 堆共享 vs 棧隔離 |
| String 能作為「輸出參數」嗎? | 不能(不可變,無法修改內容、無法換引用) | 不可變性 |
| 如何實現類似 C++ 引用傳遞效果? | 封裝類 / 數組 / 返回值 / AtomicReference / 回調接口 | 變通方案 |
一句話記住:Java 傳遞的是「遙控器副本」(引用值),按遙控器能操作電視(修改對象狀態),但換遙控器(修改引用指向)只換了副本,原遙控器仍在調用者手中。