面向對象設計的核心原則之一是「將發生變化的事物與保持不變的事物分離」。Java 通過包管理命名空間與組件聚合,通過訪問修飾符控制類成員的可見性,構建起「接口與實現分離」的物理邊界。本文系統梳理包的命名規範、編譯單元結構、四大訪問級別語義差異,並給出庫設計者的實戰準則。
慣例:`com.company.project.module`。因特網域名全球唯一(InterNIC 保證),反轉後作為包名前綴,從根本上消除命名衝突。即使無域名,亦應用極低重名概率的標識(如個人英文名全拼)。
建立 `com.toad.P` 類封裝 `System.out.print/println` 重載,編譯後置於 CLASSPATH 路徑,任意項目 `import com.toad.*` 即可使用 `P.rintln("msg")`,大幅減少樣板代碼。
Java 無 C 預處理器,但可通過切換導入包實現調試/發布雙版本:
// com.toad.Assert — 開發版:打印失敗信息
public class Assert {
public static void is_true(boolean exp) { if (!exp) System.err.println("Assertion failed"); }
public static void is_true(boolean exp, String msg) { if (!exp) System.err.println("Assertion failed: " + msg); }
}
// com.debug.Assert — 發布版:空實現,JIT 內聯後零開銷
public class Assert {
public static void is_true(boolean exp) {}
public static void is_true(boolean exp, String msg) {}
}
// 代碼中:import com.debug.*; // 或 import com.toad.*;
// Assert.is_true(x > 0, "x must be positive");
| 修飾符 | 同類 | 同包(非子類) | 不同包子類 | 不同包非子類 | 核心語義 |
|---|---|---|---|---|---|
private | ✅ | ❌ | ❌ | ❌ | 實現細節,絕對封裝 |
default (package-private) | ✅ | ✅ | ❌ | ❌ | 包內協作,外部不可見 |
protected | ✅ | ✅ | ✅ | ❌ | 面向繼承的受控開放 |
public | ✅ | ✅ | ✅ | ✅ | 對外契約,穩定接口 |
class Soup {
private Soup() {} // 禁止直接 new
public static Soup makeSoup() { return new Soup(); } // 工廠方法
private static Soup ps1 = new Soup(); // 單例
public static Soup access() { return ps1; }
}
若 `Cookie.foo()` 是 `protected`,則:
包解決了「命名空間隔離與組件聚合」,訪問修飾符解決了「實現細節封裝與接口穩定暴露」。二者協同構建了 Java 大規模工程化的基石。作為庫設計者,請記住:盡可能收緊訪問權限,只暴露不可變的契約——這是通往健壯、可維護、可演進代碼的唯一捷徑。