前言
《Java 核心技術 卷 I》第 4 章「物件與類」是 Java 面向對象基礎的權威闡述。本文將書中龐大圖表知識體系化,涵蓋類設計、靜態成員、建構子、包訪問權限、final、內部類、代理模式等核心主題。
一、類設計核心原則
1.1 封裝與資料隱藏
- 私有欄位:所有實例欄位 `private`
- 公共方法:提供精確的存取器(getter/setter)
- 不可變類:`final` 類、`final` 欄位、無 setter、防禦性複製、安全發布
1.2 類之間的關係
| 關係 | UML | 代碼體現 | 耦合度 |
| 依賴 | 虛線箭頭 | 方法參數、區域變數、靜態方法呼叫 | 最弱 |
| 關聯 | 實線箭頭 | 欄位引用 | 中等 |
| 聚合 | 空心菱形 | 整體-部分,生命週期獨立 | 中強 |
| 組合 | 實心菱形 | 整體-部分,生命週期綁定 | 最強 |
| 繼承 | 空心三角箭頭 | extends | 極強(破壞封裝) |
| 實現 | 虛線三角箭頭 | implements | 弱(面向介面) |
二、靜態成員與靜態初始化
2.1 靜態欄位與方法
- 屬於類,不屬於實例,類載入時初始化
- 靜態方法:**不能**存取 `this`、`super`、非靜態欄位/方法
- 常見用途:常數、工廠方法、工具方法、單例模式
2.2 靜態初始化區塊
class DatabaseConfig {
static String URL;
static String USER;
static {
// 載入配置檔、建立連線池等一次性操作
Properties props = new Properties();
try (InputStream in = DatabaseConfig.class.getResourceAsStream("/db.properties")) {
props.load(in);
URL = props.getProperty("url");
USER = props.getProperty("user");
} catch (IOException e) {
throw new ExceptionInInitializerError(e);
}
}
}
2.3 初始化順序鐵律
- 父類靜態欄位/區塊(按宣告順序)
- 子類靜態欄位/區塊(按宣告順序)
- 父類實例欄位/區塊
- 父類建構子
- 子類實例欄位/區塊
- 子類建構子
三、建構子深度解析
3.1 建構子鏈
class Employee {
private String name;
private double salary;
private LocalDate hireDay;
// 主建構子
public Employee(String name, double salary, int year, int month, int day) {
this.name = name;
this.salary = salary;
this.hireDay = LocalDate.of(year, month, day);
}
// 便利建構子 → 呼叫主建構子
public Employee(String name) {
this(name, 0, 2024, 1, 1); // this(...) 必須第一行
}
}
3.2 物件創建完整流程
- 分配記憶體,初始化為 0/null/false
- 按宣告順序執行實例欄位初始化、實例初始化區塊
- 執行建構子本體代碼
- 回傳物件參考
四、包與訪問權限
4.1 訪問修飾符總表
| 修飾符 | 同類 | 同包 | 子類(任意包) | 所有類 |
| private | Y | N | N | N |
| package-private | Y | Y | N | N |
| protected | Y | Y | Y | N |
| public | Y | Y | Y | Y |
4.2 設計建議
- 欄位永遠 `private`
- 方法預設 `package-private`,需外部使用才 `public`
- `protected` 僅用於「子類需覆寫/存取」的內部實現細節
- 避免 `public` 欄位(常數除外:`public static final`)
五、final 關鍵字三重語義
| 修飾對象 | 語義 | 注意事項 |
| final 類 | 不可被繼承 | String、Integer、LocalDate 等不可變類 |
| final 方法 | 不可被覆寫 | 私有方法隱含 final;效能優化(內聯) |
| final 欄位 | 引用不可變(基本型值不可變,物件引用不可變) | 必須初始化:宣告時/建構子/實例區塊;blank final |
六、內部類完整分類
6.1 四種類型對比
| 類型 | 定義位置 | 存取外部類 | 靜態成員 | 實例化語法 | 典型用途 |
| 成員內部類 | 類內部、方法外 | 隱含外部類引用,可存取所有成員 | 不可有(常數除外) | outer.new Inner() | 迭代器、事件監聽 |
| 靜態嵌套類 | 類內部、方法外、static | 僅存取外部類靜態成員 | 可有 | new Outer.StaticNested() | Builder、工具類、緩存 Entry |
| 區域內部類 | 方法/區塊內部 | 存取 final/效能最終區域變數 | 不可有 | 區域內 new LocalClass() | 複雜邏輯封裝、回調 |
| 匿名內部類 | 表達式中 | 同區域內部類 | 不可有 | new Interface/Class() { ... } | 事件處理、Runnable、Comparator |
6.2 內部類編譯產物
OuterClass.class
OuterClass$InnerClass.class // 成員內部類
OuterClass$StaticNested.class // 靜態嵌套類
OuterClass$1LocalClass.class // 區域內部類(數字編號)
OuterClass$1.class // 匿名內部類
七、代理模式與動態代理
7.1 靜態代理
interface Service { void execute(); }
class RealService implements Service { public void execute() { /* 核心邏輯 */ } }
class ProxyService implements Service {
private RealService real;
public ProxyService(RealService real) { this.real = real; }
public void execute() {
log("before"); // 橫切關注點
real.execute();
log("after");
}
}
7.2 JDK 動態代理
Service real = new RealService();
Service proxy = (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(),
new Class[]{Service.class},
(proxyObj, method, args) -> {
log("before");
Object result = method.invoke(real, args);
log("after");
return result;
}
);
- **前提**:目標類必須實現介面
- **機制**:運行時生成實現介面的代理類,委派給 `InvocationHandler`
- **限制**:只能代理介面方法
7.3 CGLIB 動態代理(Spring 預設)
- 繼承目標類,覆寫非 final 方法
- 無需介面,但無法代理 `final` 類/方法
- Spring Boot 2.0+ 預設 `spring.aop.proxy-target-class=true`
八、類設計技巧清單
- 單一職責:類只有一個變化原因
- 封裝變化:識別變化點,封裝為介面/抽象類
- 優先組合:`Has-A` 優於 `Is-A`
- 面向介面:依賴抽象,不依賴具體
- 不可變優先:減少可變狀態,簡化併發
- 文檔契約:Javadoc 記錄前置條件、後置條件、副作用、執行緒安全性
- 最小驚訝:API 行為符合直覺,避免副作用
總結
類是 Java 程式的基本構建塊。掌握封裝邊界、靜態/實例初始化順序、建構子鏈、訪問權限矩陣、final 三重語義、內部類四種類型、靜態/動態代理機制,並遵循 SOLID 設計原則,才能寫出可維護、可擴展、高內聚低耦合的專業級 Java 代碼。