首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 參數傳遞機制深度解析:值傳遞鐵律、物件引用傳值、不可變物件陷阱與最佳實踐

     編輯:Java 程式設計入門

鐵律:Java 只有值傳遞

Java 參數傳遞永遠是值傳遞 (Call by Value)。無例外。

  • 基本類型:傳遞數值副本,方法內修改不影響原變數
  • 引用類型:傳遞引用副本 (記憶體地址),方法內可修改物件內部狀態,但無法改變原引用指向

基本類型:純值傳遞


public class PrimitivePassByValue {
    public static void modify(int a, int b) {
        int temp = a;
        a = b;
        b = temp;
        System.out.println("方法內: a=" + a + ", b=" + b); // 交換成功
    }

    public static void main(String[] args) {
        int x = 10, y = 20;
        System.out.println("調用前: x=" + x + ", y=" + y); // 10, 20
        modify(x, y);
        System.out.println("調用後: x=" + x + ", y=" + y); // 仍為 10, 20 (未變)
    }
}

記憶體圖解:棧幀中基本類型區域變數獨立,修改只影響副本。

引用類型:引用值傳遞 (Call by Sharing)


class Book {
    String title;
    Book(String t) { this.title = t; }
}

public class ReferencePassByValue {
    // 1. 修改物件內部狀態:生效
    static void changeTitle(Book book, String newTitle) {
        book.title = newTitle; // 通過引用副本修改同一物件
    }

    // 2. 嘗試改變引用指向:無效
    static void reassign(Book book) {
        book = new Book("新書"); // 只改變局部引用副本指向
    }

    // 3. 交換引用:無效
    static void swap(Book a, Book b) {
        Book temp = a;
        a = b;
        b = temp; // 只交換局部引用副本
    }

    public static void main(String[] args) {
        Book b1 = new Book("Java核心技術");
        Book b2 = new Book("Effective Java");

        System.out.println("調用前: b1=" + b1.title + ", b2=" + b2.title);
        
        changeTitle(b1, "Java編程思想");
        System.out.println("changeTitle後: b1=" + b1.title); // 生效:Java編程思想

        reassign(b1);
        System.out.println("reassign後: b1=" + b1.title); // 無效:仍為 Java編程思想

        swap(b1, b2);
        System.out.println("swap後: b1=" + b1.title + ", b2=" + b2.title); // 無效:未交換
    }
}

不可變物件陷阱:String、包裝類、BigDecimal


public class ImmutableTrap {
    static void modifyString(String s) {
        s = s + " modified"; // 字串拼接產生新物件,s 指向新物件
    }

    static void modifyInteger(Integer i) {
        i = i + 1; // 自動拆箱→運算→自動裝箱產生新 Integer,i 指向新物件
    }

    static void modifyList(List list) {
        list.add("new"); // 修改列表內容:生效 (列表可變)
        // list = new ArrayList<>(); // 重新賦值:無效
    }

    public static void main(String[] args) {
        String str = "original";
        modifyString(str);
        System.out.println(str); // "original" (未變)

        Integer num = 10;
        modifyInteger(num);
        System.out.println(num); // 10 (未變)

        List list = new ArrayList<>(Arrays.asList("a"));
        modifyList(list);
        System.out.println(list); // [a, new] (生效)
    }
}

核心:不可變物件的「修改」實為建立新物件,原引用仍指向舊物件。

實現「引用傳遞效果」的三種模式

1. 返回新值 (函數式風格)


String process(String input) {
    return input.trim().toUpperCase(); // 返回新字串
}
// 調用: str = process(str);

2. 可變容器/持有者模式


// 通用持有者
class Holder { T value; }

// 或使用陣列 (長度 1)
String[] holder = { "initial" };
void modify(String[] h) { h[0] = "modified"; }
// 調用: modify(holder); // holder[0] 變為 "modified"

3. 修改可變物件內部狀態 (標準 Java 風格)


void populate(Map map) {
    map.put("key", "value"); // 修改 Map 內容
}
void addItems(List list, Item... items) {
    Collections.addAll(list, items); // 修改 List 內容
}

方法參數設計最佳實踐

場景建議範例
輸入參數基本類型、不可變物件、介面類型void process(String id, List items)
輸出參數返回值、可變容器參數、Builder 模式Result process(Input in) 或 void fill(Result out)
大量參數參數物件 (Parameter Object) / Buildervoid query(QueryParams params)
可選參數重載、Builder、Optional、Mapvoid config(ConfigBuilder b)
線程安全不可變物件、防禦性複製、ThreadLocalvoid process(ImmutableData data)

防禦性複製:保護內部狀態


public final class Period {
    private final Date start;
    private final Date end;

    public Period(Date start, Date end) {
        // 構造時防禦性複製:防止外部修改傳入的 Date
        this.start = new Date(start.getTime());
        this.end = new Date(end.getTime());
        if (this.start.compareTo(this.end) > 0) 
            throw new IllegalArgumentException("start after end");
    }

    public Date getStart() {
        // 返回時防禦性複製:防止調用者修改內部 Date
        return new Date(start.getTime());
    }
    // 同理 getEnd()
}

常見面試陷阱題


// 題 1
String s = "hello";
modify(s);
System.out.println(s); // ?

void modify(String s) { s = "world"; }
// 答:hello (引用傳值,s 指向新物件)

// 題 2
int[] arr = {1,2,3};
modify(arr);
System.out.println(Arrays.toString(arr)); // ?

void modify(int[] arr) { arr[0] = 99; }
// 答:[99, 2, 3] (修改陣列內容,引用同一陣列物件)

// 題 3
int[] arr = {1,2,3};
modify(arr);
System.out.println(Arrays.toString(arr)); // ?

void modify(int[] arr) { arr = new int[]{4,5,6}; }
// 答:[1, 2, 3] (重新賦值只改變局部引用)

// 題 4
List list = new ArrayList<>();
modify(list);
System.out.println(list.isEmpty()); // ?

void modify(List list) { list = new ArrayList<>(); list.add("x"); }
// 答:true (重新賦值無效)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved