CodeGym /課程 /JAVA 25 SELF /初始化區塊

初始化區塊

JAVA 25 SELF
等級 15 , 課堂 4
開放

1. 什麼是初始化區塊

在 Java 中有兩種初始化區塊:

  • 實例(非靜態)初始化區塊 — 在每次建立新物件時執行,發生在欄位初始化之後、建構子呼叫之前。
  • 靜態初始化區塊 — 在類別載入至記憶體時執行一次(在建立第一個物件或存取靜態欄位/方法之前)。

實例初始化區塊

在類別本體中直接宣告,不需要像 static 之類的關鍵字:

public class User {
    private String name;

    // 實例初始化區塊
    {
        System.out.println("正在執行實例初始化區塊!");
        name = "預設名稱";
    }

    public User() {
        System.out.println("正在執行建構子!");
    }
}

靜態初始化區塊

使用關鍵字 static 宣告:

public class Config {
    public static String appName;

    static {
        System.out.println("正在執行靜態初始化區塊!");
        appName = "我的超級應用程式";
    }
}

2. 初始化順序:誰先誰後?

在 Java 中,類別元素的初始化順序不是隨便亂來,而是有嚴格定義的序列。若你曾試過不看說明書就組裝 IKEA 家具,就能理解為什麼順序很重要:步驟一旦弄錯,衣櫃就可能變成藝術品。

初始化的順序:

  1. 靜態欄位與靜態區塊 — 依宣告順序,類別載入時執行一次。
  2. 實例欄位與實例區塊 — 依宣告順序,每次建立新物件時執行。
  3. 建構子 — 在所有實例初始化之後執行。

示意圖

+-------------------------------+
|   將類別載入 JVM              |
+-------------------------------+
| 1. 靜態欄位                   |
| 2. 靜態區塊                   |
|         ↓                     |
|   建立物件                    |
|         ↓                     |
| 3. 實例欄位                   |
| 4. 實例區塊                   |
| 5. 建構子                     |
+-------------------------------+

範例:輸出順序

我們寫個類別來示範實際的執行順序:

public class Demo {
    static String staticField = print("1. static 欄位");

    static {
        print("2. static 區塊");
    }

    String field = print("3. 實例欄位");

    {
        print("4. 實例區塊");
    }

    public Demo() {
        print("5. 建構子");
    }

    static String print(String msg) {
        System.out.println(msg);
        return msg;
    }

    public static void main(String[] args) {
        System.out.println("建立第一個 Demo 物件:");
        Demo d1 = new Demo();

        System.out.println("\n建立第二個 Demo 物件:");
        Demo d2 = new Demo();
    }
}

畫面上會看到什麼?

1. static 欄位
2. static 區塊
建立第一個 Demo 物件:
3. 實例欄位
4. 實例區塊
5. 建構子

建立第二個 Demo 物件:
3. 實例欄位
4. 實例區塊
5. 建構子

請注意: 靜態部分(static)只會執行一次 —— 在首次存取類別時。凡是不屬於 static 的,則會在每次建立物件時執行。

3. 程式碼範例:為什麼要用初始化區塊

當建構子不夠用時

有時候,部分初始化必須在所有建構子中都一致執行。比方說類別有多個建構子,而你不想在每個建構子裡重複相同的初始化。這時就很適合把它提到實例初始化區塊:

public class Person {
    private String id;
    private String name;

    {
        // 此段程式碼會在任何建構子之前執行
        id = java.util.UUID.randomUUID().toString();
        System.out.println("產生唯一的 id: " + id);
    }

    public Person() {
        System.out.println("Person() 無參數");
    }

    public Person(String name) {
        this.name = name;
        System.out.println("Person(String name)");
    }
}

結果: 建立任何 Person 物件時,會先產生 id,接著才會執行對應的建構子。

初始化複雜的靜態資料

靜態區塊常用於初始化「沈重」或複雜的靜態欄位,例如從檔案讀取設定、建立集合、連線到資料庫等。

public class Settings {
    public static final java.util.Map<String, String> DEFAULTS;

    static {
        DEFAULTS = new java.util.HashMap<>();
        DEFAULTS.put("theme", "light");
        DEFAULTS.put("language", "ru");
        System.out.println("Settings 的靜態區塊:預設設定");
    }
}

4. 實用細節

何時該使用初始化區塊

適用情境

  • 當你需要在所有建構子之前做共用初始化時。
  • 當靜態資料很複雜,無法用單純賦值表達時。
  • 用來初始化靜態資源(例如應用程式啟動時讀取設定檔)。

不建議使用的情況

  • 若可用一般的賦值或建構子解決,就用那些方式。
  • 不要把商業邏輯藏在初始化區塊裡 —— 會讓程式難讀、難維護。
  • 如果初始化仰賴建構子的參數,請在建構子內進行。

別過度使用初始化區塊

初始化區塊很強大,但並非常用工具。大多數情況下,單純賦值或建構子就足夠。若類別裡有太多初始化區塊,程式會變得難以閱讀與維護。

不要在實例初始化區塊處理依賴建構子參數的邏輯

實例初始化區塊在建構子之前執行,因此無法使用建構子的參數。若需要根據參數進行初始化,請在建構子內處理。

靜態區塊與繼承

靜態初始化區塊不會被繼承。每個類別都有自己的靜態區塊。當載入子類別時,會先執行父類別的靜態區塊,然後才是子類別的靜態區塊。

5. 使用初始化區塊的常見錯誤

錯誤 1:以為實例初始化區塊能取得建構子參數。
許多新手會嘗試在實例初始化區塊中使用建構子參數,但結果不是編譯錯誤,就是得到出乎意料的結果。請記住:實例初始化區塊在建構子之前執行,因此當下還沒有任何參數。

錯誤 2:在初始化區塊裡放入過多邏輯。
若初始化區塊中出現複雜邏輯,程式就會變得混亂。建議把主要工作放在建構子或獨立方法中。

錯誤 3:多個靜態區塊的宣告順序造成問題.
若類別中有多個靜態區塊,它們會依照在程式碼中的宣告順序與靜態欄位一起執行。若其中一個區塊依賴另一個區塊的結果,可能導致出乎意料的行為。

錯誤 4:以為靜態區塊會在每次建立物件時執行。
static 區塊只會在類別載入時執行一次。若你期待重複初始化,這是不會發生的。

錯誤 5:嘗試在靜態區塊中存取實例欄位。
在靜態區塊中只能使用靜態變數與方法。嘗試存取實例(一般)欄位會導致編譯錯誤。

1
問卷/小測驗
封裝,等級 15,課堂 4
未開放
封裝
封裝的原則
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION