package Jan2017;
public class Ques0113 {
private final String id; // final 實例字段
public Ques0113() {
System.out.println("構造函數");
}
public void setId(String id) {
id = id; // 參數遮蔽字段,賦值給參數自身而非字段
}
}
final 實例字段**必須**在以下位置之一**確定賦值** (definitely assigned):
private final String id = "default";{ id = "init"; }this(...) 中)id 無聲明賦值、無初始化塊、構造器 Ques0113() 未賦值 → **編譯錯誤**:variable id might not have been initialized。public void setId(String id) { // 參數 id 遮蔽字段 this.id
id = id; // 將參數賦值給參數自身,無任何效果
// 正確寫法:this.id = id; (但 final 字段不可在構造器外修改,所以此方法對 final 字段無效)
}
即使編譯通過,setId 也無法修改 final 字段,且參數遮蔽導致邏輯空轉。
public class Ques0113Fixed {
private final String id;
// 方案一:構造器初始化 (推薦,不可變對象模式)
public Ques0113Fixed(String id) {
this.id = id; // 明確賦值給字段
System.out.println("構造函數: " + id);
}
// 方案二:聲明處初始化
// private final String id = "default";
// final 字段不可提供 setter,若需修改請去掉 final
public String getId() { return id; }
public static void main(String[] args) {
new Ques0113Fixed("123"); // 正常編譯運行
}
}
| 概念 | 規則 | 本題體現 |
|---|---|---|
| final 實例字段 | 構造器結束前必須確定賦值一次 | 構造器未賦值 → 編譯失敗 |
| final 靜態字段 | 靜態初始化塊/聲明處賦值,類加載階段完成 | — |
| 參數遮蔽 | 方法參數/局部變量同名字段時遮蔽,需用 this. 區分 | setId 中 id = id 無效 |
| 不可變對象 | final 字段 + 無 setter + 構造器注入 = 線程安全共享 | 修正版體現 |
id 改為非 final,代碼能編譯通過嗎?能,但 setId 仍有遮蔽 Bug。setId("x") 能否滿足 final 初始化要求?不能,編譯器不做方法內部流分析,仍報未初始化。final 引用類型字段指向的對象內部狀態可變嗎?可變,final 只保證引用不變,不保證對象不可變。