對象創建與銷毀是 Java 程序的基石,直接影響性能、可維護性與穩定性。本文系統梳理五種創建方式、靜態工廠優缺點、Builder 模式適用場景、單例實現演進、不可實例化類設計、避免創建無用對象、消除過期引用、終結方法慎用等核心實踐,參考《Effective Java》精華並結合生產經驗。
new ClassName()。Boolean.valueOf(true)。build()。Cloneable,淺/深拷貝現有對象。Serializable,從字節流恢復對象。fromString()、getInstance() 比構造器語義清晰。Collections.emptyList() 返回不可變列表)。Map<String,List<String>> m = HashMap.newInstance();。public/protected 構造器則無法子類化(如 Collections、Arrays)。當構造器參數 ≥ 4 時,Builder 模式優於重疊構造器與 JavaBeans 模式。
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
// 必填參數
private final int servingSize;
private final int servings;
// 選填參數,默認值
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) { calories = val; return this; }
public Builder fat(int val) { fat = val; return this; }
public Builder carbohydrate(int val) { carbohydrate = val; return this; }
public Builder sodium(int val) { sodium = val; return this; }
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
this.servingSize = builder.servingSize;
this.servings = builder.servings;
this.calories = builder.calories;
this.fat = builder.fat;
this.sodium = builder.sodium;
this.carbohydrate = builder.carbohydrate;
}
}
// 客戶端調用
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
.calories(100).sodium(35).carbohydrate(27).build();
build() 中可驗證約束(如 servingSize > 0)。參數 ≥ 4 時使用;參數少用構造器/工廠更簡潔。
public static final Instance INSTANCE = new Instance();(公有靜態最終字段)private static final Instance INSTANCE = new Instance(); public static Instance getInstance()(靜態工廠)public enum Elvis {
INSTANCE;
public void leaveTheBuilding() { ... }
}
readResolve)。工具類(如 java.lang.Math、Collections)不應被實例化:
public class UtilityClass {
private UtilityClass() {
throw new AssertionError("禁止實例化");
}
// 靜態方法...
}
副作用:導致類不可被子類化(子類構造器必須調用父類構造器,但父類構造器私有)。工具類本不應被繼承,此副作用可接受。
String s = "literal" 而非 new String("literal");Boolean.valueOf() 替代 new Boolean()。Date 格式化器)。long sum = 0L; 而非 Long sum = 0L;(循環中差異巨大)。Integer.valueOf() 緩存 -128~127。Java 有 GC,但類管理內存時(如自定義棧、緩存、監聽器)需手動清空引用:
public class Stack {
private Object[] elements;
private int size = 0;
public Object pop() {
if (size == 0) throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // 關鍵:清空引用,讓 GC 回收
return result;
}
}
WeakHashMap 或 Guava Cache(基於弱引用/軟引用 + 過期策略)。WeakReference<Listener>)。close()、shutdown(),配合 try-with-resources(實現 AutoCloseable)保證及時釋放。finalize() 中記錄日誌警告「資源未顯式關閉」,不執行核心釋放邏輯。若覆寫 finalize(),必須調用 super.finalize()(放在 finally 塊),否則父類終結邏輯不執行。
| 場景 | 推薦做法 | 避免做法 |
|---|---|---|
| 少參數構建 | 構造器 / 靜態工廠 | new 重復字面量 |
| 多參數構建 | Builder 模式 | 重疊構造器 / JavaBeans(半初始化態) |
| 單例 | 枚舉類型 | 雙重檢查鎖 / 靜態內部類(可接受但非最佳) |
| 工具類 | 私有構造器拋異常 | 公有無參構造器 |
| 頻繁創建對象 | 基本類型 / 靜態工廠復用 / 對象池 | 循環中 new 包裝類型 |
| 管理內存組件 | 手動清空引用 / 弱引用緩存 | 依賴 GC 自動處理 |
| 資源釋放 | try-with-resources + AutoCloseable | 依賴 finalize() |
對象創建與銷毀看似基礎,實則是系統穩定性與性能的基石。遵循《Effective Java》原則,選擇正確的創建模式、嚴守不可變性、科學管理引用生命週期,是資深 Java 工程師的必修內功。