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

Java 對象創建與銷毀最佳實踐:構造器、工廠、Builder、單例與內存管理

     編輯:Java 相關知識

前言

對象創建與銷毀是 Java 程序的基石,直接影響性能、可維護性與穩定性。本文系統梳理五種創建方式、靜態工廠優缺點、Builder 模式適用場景、單例實現演進、不可實例化類設計、避免創建無用對象、消除過期引用、終結方法慎用等核心實踐,參考《Effective Java》精華並結合生產經驗。

一、 對象創建的五種方式

  1. 構造器:最基本方式,new ClassName()。
  2. 靜態工廠方法:類提供靜態方法返回實例,如 Boolean.valueOf(true)。
  3. Builder 模式:針對多參數構建,鏈式調用,最后 build()。
  4. clone():實現 Cloneable,淺/深拷貝現有對象。
  5. 反序列化:實現 Serializable,從字節流恢復對象。

二、 靜態工廠方法:優於構造器的選擇

優點

  • 有名稱:fromString()、getInstance() 比構造器語義清晰。
  • 可控制實例創建:單例、享元、延遲初始化、返回子類型/接口實現(如 Collections.emptyList() 返回不可變列表)。
  • 簡化參數化類型實例化:Java 7+ 鑽石操作符前,工廠可省略泛型參數:Map<String,List<String>> m = HashMap.newInstance();。

缺點

  • 無 public/protected 構造器則無法子類化(如 Collections、Arrays)。
  • 與普通靜態方法無區別,API 文檔需明確標識「工廠方法」。

三、 多參數構建:Builder 模式詳解

當構造器參數 ≥ 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 時使用;參數少用構造器/工廠更簡潔。

四、 單例模式:枚舉為王

演進歷程

  1. public static final Instance INSTANCE = new Instance();(公有靜態最終字段)
  2. private static final Instance INSTANCE = new Instance(); public static Instance getInstance()(靜態工廠)
  3. 枚舉單例(最佳):
public enum Elvis {
    INSTANCE;
    public void leaveTheBuilding() { ... }
}

枚舉優勢

  • 語法簡潔,天然防反射攻擊(構造器私有化可被反射破壞,枚舉不可)。
  • 天然防序列化破壞(JVM 保證枚舉常量單例,無需 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>)。

八、 慎用終結方法

缺陷

  • 執行時間不確定,可能延遲無限久(甚至程序退出前才運行)。
  • 異常被忽略,無法傳播。
  • 嚴重降低 GC 性能(需進入終結隊列、專用線程處理)。

替代方案

  • 顯式關閉方法:close()、shutdown(),配合 try-with-resources(實現 AutoCloseable)保證及時釋放。
  • 作為安全網:僅在 finalize() 中記錄日誌警告「資源未顯式關閉」,不執行核心釋放邏輯。
  • 清理非關鍵本地資源:如臨時文件、本地緩存。

注意

若覆寫 finalize(),必須調用 super.finalize()(放在 finally 塊),否則父類終結邏輯不執行。

九、 總結清單

場景推薦做法避免做法
少參數構建構造器 / 靜態工廠new 重復字面量
多參數構建Builder 模式重疊構造器 / JavaBeans(半初始化態)
單例枚舉類型雙重檢查鎖 / 靜態內部類(可接受但非最佳)
工具類私有構造器拋異常公有無參構造器
頻繁創建對象基本類型 / 靜態工廠復用 / 對象池循環中 new 包裝類型
管理內存組件手動清空引用 / 弱引用緩存依賴 GC 自動處理
資源釋放try-with-resources + AutoCloseable依賴 finalize()

對象創建與銷毀看似基礎,實則是系統穩定性與性能的基石。遵循《Effective Java》原則,選擇正確的創建模式、嚴守不可變性、科學管理引用生命週期,是資深 Java 工程師的必修內功。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved