private class ClientHandler implements Runnable {
private Socket socket;
public void run() {
PrintWriter writer = null; // 關鍵點:這裡為什麼不在 try 中直接賦值?
try {
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "utf-8");
writer = new PrintWriter(osw, true);
addOut(writer); // 註冊到共享集合
// 處理通信循環...
} catch (Exception e) {
e.printStackTrace();
} finally {
removeOut(writer); // 此處需要訪問 writer
if (socket != null) {
try { socket.close(); } catch (Exception e) { e.printStackTrace(); }
}
}
}
}
| 聲明位置 | 作用域範圍 | finally 可訪問 | 編譯結果 |
|---|---|---|---|
try 代碼塊內 | 僅限 try 塊 | ❌ 編譯錯誤 | 變量未定義 |
try 前聲明並初始化為 null | 整個方法體 | ✅ 正常訪問 | 編譯通過 |
removeOut(writer)、socket.close())finally 中使用的局部變量必須在 try 之前聲明並確定賦值Resource resource = null;
try {
resource = acquireResource();
// 使用資源
} finally {
if (resource != null) {
releaseResource(resource);
}
}
使用 try-with-resources 自動管理資源生命周期:
try (PrintWriter writer = new PrintWriter(
new OutputStreamWriter(socket.getOutputStream(), "utf-8"), true)) {
addOut(writer);
// 通信處理...
} catch (Exception e) {
e.printStackTrace();
} finally {
removeOut(writer); // writer 在此仍可訪問(隱式 final)
}
注意:try-with-resources 會自動關閉實現 AutoCloseable 的資源,但共享集合註冊/註銷邏輯仍需手動管理。
PrintWriter writer = null 在 try 之前聲明是為了讓 finally 塊能訪問該變量執行資源清理,這是 Java 資源管理的經典模式,現代代碼建議遷移至 try-with-resources。