@throws 標記說明違反限制時拋出的異常。BigInteger.mod() 檢查模數必須為正,否則拋出 ArithmeticException。assert) 檢查參數。-ea 或 -enableassertions;IntelliJ IDEA 在 VM Options 加入 -ea。核心觀念:假設客戶端會盡全力破壞類別的約束條件,進行防禦性程式設計。
看似不可變,但 Date 可變,客戶端可透過取得的參考修改內部狀態:
// 攻擊 1:建構子參數
Date start = ...; Date end = ...;
Period p = new Period(start, end);
start.setYear(2017); // 修改了 Period 內部狀態
// 攻擊 2:存取器回傳可變內部參考
p.getEnd().setYear(1998); // 修改了 Period 實例
new Date(start.getTime()),複製後再驗證約束。return new Date(start.getTime())。Map 鍵值陷阱:使用可變物件 (如 Date) 作為 Map Key,修改後導致無法查找。解決:用不可變值 (如 date.getTime() 的 long 值) 作為 Key。
Map 而非 HashMap)TemperatureScale {F, C})重載解析是靜態的(編譯期決定),覆寫是動態的(執行期決定)。
class CollectionClassifier {
static String classify(Set> s) { return "Set"; }
static String classify(List> l) { return "List"; }
static String classify(Collection> c) { return "Unknown"; }
}
Collection>[] cs = { new HashSet<>(), new ArrayList<>(), new HashMap<>().values() };
for (Collection> c : cs) System.out.println(classify(c));
// 輸出三次 "Unknown"!因為編譯期類型皆為 Collection>
重載方法選擇在編譯期根據宣告類型決定。避免同類型參數的重載,或確保不會產生歧義。
Wine[] wines = { new Wine(), new SparklingWine(), new Champagne() };
for (Wine w : wines) System.out.println(w.name());
// 輸出:wine, sparkling wine, champagne
// 覆寫方法在執行期根據實際物件類型動態分派
嚴謹的參數驗證、必要時的防禦性複製、清晰的方法簽名、謹慎使用重載,是編寫健壯、可維護 API 的關鍵。區分靜態重載與動態覆寫的語意差異,避免陷阱。