你是否遇到過這種情況:明明調用了 add() 方法,結果卻沒變?這不是 Bug,而是 不可變對象 的核心特性。本文以 BigInteger 為切入點,徹底講透不可變模式的原理、優勢與正確使用姿勢。
BigInteger total = BigInteger.ZERO;
total.add(new BigInteger("5000"));
total.add(new BigInteger("50000"));
total.add(new BigInteger("500000"));
System.out.println(total); // 輸出 0,而非預期的 555000
原因:BigInteger 實例一旦創建,內部數值 永不改變。所有運算方法(add、multiply 等)均返回 新實例,原對象保持不變。
// 方式一:逐步賦值
total = total.add(new BigInteger("5000"));
total = total.add(new BigInteger("50000"));
total = total.add(new BigInteger("500000"));
// 方式二:鏈式調用(推薦)
BigInteger total = BigInteger.ZERO
.add(new BigInteger("5000"))
.add(new BigInteger("50000"))
.add(new BigInteger("500000"));
System.out.println(total); // 555000
Integer、Long、Double 等BigInteger、BigDecimalLocalDate、Instant 等(Java 8+)public final class ImmutablePoint { // 1. final 類,防繼承破壞
private final int x; // 2. 所有字段 final + private
private final int y;
public ImmutablePoint(int x, int y) { // 3. 構造器初始化所有字段
this.x = x;
this.y = y;
}
// 4. 僅提供 getter,無 setter
public int getX() { return x; }
public int getY() { return y; }
// 5. 「修改」操作返回新實例
public ImmutablePoint translate(int dx, int dy) {
return new ImmutablePoint(x + dx, y + dy);
}
// 6. 防禦性拷貝(若持有可變組件)
// private final Date date;
// public Date getDate() { return new Date(date.getTime()); }
}
String s = "a"; s += "b"; 循環中拼接 → ✅ 用 StringBuilderBigDecimal price = new BigDecimal(0.1); 浮點精度陷阱 → ✅ 用 new BigDecimal("0.1")List、Date)直接暴露給外部 → ✅ 返回防禦性拷貝或不可變視圖不可變對象不是「不能用」,而是「要用對方式」——把操作結果賦值回引用。這種設計哲學貫穿 Java 核心庫與現代框架,掌握它能寫出更健壯、並發友好的代碼。