Java 類別層級的存取控制只有兩種選擇:public 與 package-private(無修飾詞)。本文將詳細說明類別存取規則、檔案命名限制、單一 public 類別限制,並深入探討如何透過 private 建構子實現單例、工廠方法等進階建模式。
| 修飾詞 | 可見範圍 | 適用場景 |
|---|---|---|
public | 所有類別(跨套件) | 對外公開的 API、主要介面類別 |
package-private (無修飾詞) | 同一套件內的類別 | 內部實作類別、輔助類別、測試專用類別 |
注意:類別不可宣告為 private 或 protected(巢狀類別除外,詳見內部類別章節)。
public 類別public class Widget → 檔名必須為 Widget.java(大小寫敏感)違反任一規則皆導致編譯錯誤。
移除 public 修飾詞,類別即變為 package-private:
new、繼承、存取其 package-private 成員將**所有**建構子宣告 private,外部無法直接 new,必須透過類別提供的靜態方法取得實例。兩大經典模式:
class Soup {
private Soup() {} // 阻止直接 new
public static Soup makeSoup() { // 受控建立
// 可在此加入:數量限制、參數驗證、日誌、池化等邏輯
return new Soup();
}
}
// 使用端
Soup s = Soup.makeSoup(); // 唯一合法取得途徑
class Soup {
private Soup() {}
private static Soup instance = new Soup(); // 類別載入時建立唯一實例
public static Soup access() { // 全域存取點
return instance;
}
public void f() {} // 業務方法
}
// 使用端
Soup.access().f(); // 取得唯一實例並呼叫方法
即使類別不可被外部實例化,其 public static 欄位/方法仍可被外部存取:
class Hidden {
public static final String CONSTANT = "value";
public static void utility() { ... }
}
// 外部套件
Hidden.CONSTANT; // 合法
Hidden.utility(); // 合法
// new Hidden(); // 編譯錯誤:類別不可見
同目錄下無 package 宣告的檔案,屬於同一「預設套件」,彼此視為 package-private 可見:
// Cake.java
class Cake {
public static void main(String[] args) {
Pie p = new Pie(); // 合法:同預設套件
p.f();
}
}
// Pie.java
class Pie {
void f() { System.out.println("Pie.f()"); } // package-private
}
這解釋了早期無套件練習為何能跨檔案存取類別。
publicnew,獲得建立過程的完全控制權精準掌握類別存取層級,是建立「介面清晰、實作隱蔽、演進安全」Java 系統的第一步。