一頁看完兩週

@lL0.40L0.36@ 觀念 & 一句話 & 關鍵字/範例
封裝 & 欄位 ,只透過公開方法改狀態,藉此守住資料不變量並分離介面與實作 & ; vs
介面 & 只有簽名與規格、沒有實作的契約;實作類別必須提供每個方法 & 、、不能
ADT & 一組操作加上規格,不談儲存方式;介面表達 ADT,類別是資料結構 & :環狀緩衝區 vs 鏈結串列
子型別 & 需要 U 的地方都能給 T;型別即集合,子型別即子集合 & /; 是根
繼承 & 重用父類別的欄位與方法;覆寫改行為; 呼叫父版本 & 、抽象類別、、建構子不繼承
動態分派 & 能不能呼叫看靜態型別;呼叫哪個版本看執行期型別 &
LSP & 子型別必須能替換父型別而不弄壞依賴契約的程式:要求不能變多、承諾不能變少 & 違規; 的抉擇
JUnit & 方法 + 斷言;每個測試一個新物件; 準備 & 、
黑箱測試 & 只透過介面、只依契約判斷;一套測試可測多種實作 & 抽象測試類別 + 工廠方法
TDD & 紅(先寫會失敗的測試)→ 綠(最少程式)→ 重構 & 每圈一小步;測試先失敗才有意義
轉型 & 向上自動安全;向下要明寫且執行期檢查 & 、
泛型 & 型別當參數,編譯期型別安全、免轉型;執行期被擦除 & 、、不能
Collections & 介面(List/Set/Map)+ 實作(ArrayList/HashSet/HashMap…) & 宣告用介面; 不可變;
雜湊函數 & 尊重等價、確定、快、分布均勻 & ;
雜湊表 & 鍵 → 桶索引;碰撞用鏈結或探測;負載因子超標就倍增重雜湊 & 平均 \(\bigO{1}\);/ 契約;鍵不可變
攤銷複雜度 & 總成本除以操作數;倍增讓貴的操作足夠稀少 & 陣列倍增總成本 \(< 3n\)

最常見的十二個錯誤

  1.  為每個欄位反射性地寫 getter/setter。 公開方法對應操作(),不對應欄位。

  2.  回傳內部陣列或 的參照。 防禦性複製(、)。

  3.   吞掉所有錯誤。 只接自己知道怎麼處理的例外。

  4.  忘記 ,拼錯方法名變成新方法而沒有覆寫。 永遠加 。

  5.  用 比較 、 或任何物件。 用 (或 防 null)。

  6.  為了少打字而繼承一個契約更強的類別(手續費帳戶繼承 )。 繼承表達行為上的 is-a;否則用組合。

  7.  在 static 方法裡直接用實例欄位。 需要物件狀態就寫實例方法,或把物件當參數傳入。

  8.  測試裡宣告 並讀 。 宣告成介面型別,只用公開方法。

  9.   顛倒。 預期值在前,失敗訊息才正確。

  10.  泛型類別回傳 。 回傳 ;內部用 。

  11.  覆寫 不覆寫 ;用可變物件當鍵。 兩個一起覆寫()或直接用 record。

  12.   算桶索引。 。

自我測驗(20 題)

先不看解答,每題用一兩句話回答。

  1. 封裝帶來的兩個好處是什麼?各舉 的一個例子。

  2. 應該拋出 還是 ?餘額 10 時 呢?

  3. 「 欄位只有同一個物件能存取」——對還是錯?

  4. 介面與抽象類別各在什麼情況下是較好的選擇?

  5. 為什麼無法編譯?

  6. 用一句話定義子型別。 是 的子型別嗎?為什麼?

  7. 之後呼叫 ,執行的是哪個類別的 ?這叫什麼?

  8. 建構子會被繼承嗎?子類別建構子的第一行隱含在做什麼?

  9. 列出行為子型別的四條規則。 違反哪幾條?

  10. 什麼時候違反 LSP、什麼時候不違反?

  11. JUnit 為什麼對每個 方法建立一個新的測試物件?

  12. 黑箱測試與白箱測試的差別? 裡哪一個測試其實是白箱?

  13. TDD 中「測試必須先失敗」的目的是什麼?

  14. 會在編譯期還是執行期出錯?出什麼錯?

  15. 為什麼泛型類別裡不能寫 ?標準的替代做法是什麼?

  16. 的結果是什麼?為什麼?

  17. 要「依字母順序印出每個單字的出現次數」,該用哪種 實作?若只需要次數不需順序呢?

  18. 一個好的雜湊函數需要哪四個性質?哪一個是正確性要求、哪些是效能要求?

  19. 說明鏈結雜湊表 的步驟,以及為什麼 與 必須一致。

  20. 為什麼陣列倍增的 是攤銷 \(\bigO{1}\),而每次加一格是攤銷 \(\bigO{n}\)?

  1. 完整性( 永遠 \(\ge 0\),因為只能經 / 改)與抽象(使用者只需知道三個方法;內部改成 不影響呼叫者)。

  2. (參數本身不合法);(參數合法但目前狀態不允許)。

  3. 錯。 是類別層級的界線:同類別的程式碼可以存取另一個同類別物件的私有欄位。

  4. 介面:表達能力/契約、要多重實作、想最大解耦(預設選擇)。抽象類別:多個實作共享大量欄位與具體方法。

  5. 的靜態型別是 ,契約裡沒有 ;編譯器只看靜態型別決定方法是否存在。

  6. 任何需要 U 的地方都可以安全提供 T,則 T 是 U 的子型別。是:,子型別可傳遞。

  7. 的版本(執行期型別);動態分派。

  8. 不會。隱含呼叫 ,先建構父類別那部分;父類別沒有無參數建構子時必須明寫 。

  9. 前置條件不能更強、後置條件不能更弱、不變量保持、歷史約束。違反 2(永遠回 true)與 3(餘額可負)。

  10. 有 / 的可變矩形會違反(設寬 5 高 3 期望面積 15);不可變的 record 不違反。

  11. 讓測試彼此獨立、順序無關,一個測試留下的狀態不會影響另一個。

  12. 黑箱只透過公開介面依契約判斷,可套用所有實作;白箱依賴實作細節。(介面沒規定 null 行為)。

  13. 證明測試真的在檢查目標行為(否則可能是永遠通過的無效測試),並確認新功能確實尚未存在。

  14. 執行期;。編譯器只知道 是 ,轉型合法與否要到執行期才知道。

  15. 型別擦除:執行期不知道 是什麼。用 儲存,取出時 轉型並加 ,或直接用 。

  16. (大多數 JVM):1000 超出 快取範圍(-128..127),兩個是不同物件;比較物件要用 。

  17. (依鍵排序走訪);(平均 \(\bigO{1}\),不需順序)。

  18. 尊重等價(正確性)、確定性(正確性)、快、分布均勻(後兩者效能)。

  19. 算 找桶 → 沿鏈用 比對。若相等的鍵雜湊值不同,會找錯桶而找不到。

  20. 倍增:搬移總量 \(1+2+4+\cdots < 2n\),總成本 \(< 3n\),平均常數。加一格:搬移 \(1+2+\cdots+n = \bigO{n^2}\),平均 \(\bigO{n}\)。

中英術語對照

封裝 encapsulation 抽象資料型別 abstract data type (ADT)
資料不變量 data invariant 存取修飾子 access modifier
介面 interface 實作 implementation
子型別 subtype 父類別/子類別 superclass / subclass
繼承 inheritance 覆寫 override
動態分派 dynamic dispatch 靜態/執行期型別 static / runtime type
抽象類別 abstract class 行為子型別 behavioural subtype
Liskov 替換原則 Liskov Substitution Principle 前置/後置條件 pre- / postcondition
單元測試 unit test 黑箱/白箱 black-box / white-box
測試驅動開發 test-driven development 斷言 assertion
防禦性複製 defensive copy 轉型 cast
泛型 generics 型別參數/引數 type parameter / argument
型別擦除 type erasure 自動裝箱 autoboxing
雜湊函數 hash function 雜湊表 hash table
桶 bucket 碰撞 collision
鏈結 chaining 線性探測 linear probing
負載因子 load factor 重新雜湊 rehash
攤銷複雜度 amortised complexity 通訊協定 protocol