一頁看完兩週
@lL0.40L0.36@ 觀念 & 一句話 & 關鍵字/範例
封裝 & 欄位 ,只透過公開方法改狀態,藉此守住資料不變量並分離介面與實作 & ; vs
介面 & 只有簽名與規格、沒有實作的契約;實作類別必須提供每個方法 & 、、不能
ADT & 一組操作加上規格,不談儲存方式;介面表達 ADT,類別是資料結構 & :環狀緩衝區 vs 鏈結串列
子型別 & 需要 U 的地方都能給 T;型別即集合,子型別即子集合 & /; 是根
繼承 & 重用父類別的欄位與方法;覆寫改行為; 呼叫父版本 & 、抽象類別、、建構子不繼承
動態分派 & 能不能呼叫看靜態型別;呼叫哪個版本看執行期型別 &
LSP & 子型別必須能替換父型別而不弄壞依賴契約的程式:要求不能變多、承諾不能變少 & 違規; 的抉擇
JUnit & 方法 + 斷言;每個測試一個新物件; 準備 & 、
黑箱測試 & 只透過介面、只依契約判斷;一套測試可測多種實作 & 抽象測試類別 + 工廠方法
TDD & 紅(先寫會失敗的測試)→ 綠(最少程式)→ 重構 & 每圈一小步;測試先失敗才有意義
轉型 & 向上自動安全;向下要明寫且執行期檢查 & 、
泛型 & 型別當參數,編譯期型別安全、免轉型;執行期被擦除 & 、、不能
Collections & 介面(List/Set/Map)+ 實作(ArrayList/HashSet/HashMap…) & 宣告用介面; 不可變;
雜湊函數 & 尊重等價、確定、快、分布均勻 & ;
雜湊表 & 鍵 → 桶索引;碰撞用鏈結或探測;負載因子超標就倍增重雜湊 & 平均 \(\bigO{1}\);/ 契約;鍵不可變
攤銷複雜度 & 總成本除以操作數;倍增讓貴的操作足夠稀少 & 陣列倍增總成本 \(< 3n\)
最常見的十二個錯誤
為每個欄位反射性地寫 getter/setter。 公開方法對應操作(),不對應欄位。
回傳內部陣列或 的參照。 防禦性複製(、)。
吞掉所有錯誤。 只接自己知道怎麼處理的例外。
忘記 ,拼錯方法名變成新方法而沒有覆寫。 永遠加 。
用 比較 、 或任何物件。 用 (或 防 null)。
為了少打字而繼承一個契約更強的類別(手續費帳戶繼承 )。 繼承表達行為上的 is-a;否則用組合。
在 static 方法裡直接用實例欄位。 需要物件狀態就寫實例方法,或把物件當參數傳入。
測試裡宣告 並讀 。 宣告成介面型別,只用公開方法。
顛倒。 預期值在前,失敗訊息才正確。
泛型類別回傳 。 回傳 ;內部用 。
覆寫 不覆寫 ;用可變物件當鍵。 兩個一起覆寫()或直接用 record。
算桶索引。 。
自我測驗(20 題)
先不看解答,每題用一兩句話回答。
封裝帶來的兩個好處是什麼?各舉 的一個例子。
應該拋出 還是 ?餘額 10 時 呢?
「 欄位只有同一個物件能存取」——對還是錯?
介面與抽象類別各在什麼情況下是較好的選擇?
為什麼無法編譯?
用一句話定義子型別。 是 的子型別嗎?為什麼?
之後呼叫 ,執行的是哪個類別的 ?這叫什麼?
建構子會被繼承嗎?子類別建構子的第一行隱含在做什麼?
列出行為子型別的四條規則。 違反哪幾條?
什麼時候違反 LSP、什麼時候不違反?
JUnit 為什麼對每個 方法建立一個新的測試物件?
黑箱測試與白箱測試的差別? 裡哪一個測試其實是白箱?
TDD 中「測試必須先失敗」的目的是什麼?
會在編譯期還是執行期出錯?出什麼錯?
為什麼泛型類別裡不能寫 ?標準的替代做法是什麼?
的結果是什麼?為什麼?
要「依字母順序印出每個單字的出現次數」,該用哪種 實作?若只需要次數不需順序呢?
一個好的雜湊函數需要哪四個性質?哪一個是正確性要求、哪些是效能要求?
說明鏈結雜湊表 的步驟,以及為什麼 與 必須一致。
為什麼陣列倍增的 是攤銷 \(\bigO{1}\),而每次加一格是攤銷 \(\bigO{n}\)?
完整性( 永遠 \(\ge 0\),因為只能經 / 改)與抽象(使用者只需知道三個方法;內部改成 不影響呼叫者)。
(參數本身不合法);(參數合法但目前狀態不允許)。
錯。 是類別層級的界線:同類別的程式碼可以存取另一個同類別物件的私有欄位。
介面:表達能力/契約、要多重實作、想最大解耦(預設選擇)。抽象類別:多個實作共享大量欄位與具體方法。
的靜態型別是 ,契約裡沒有 ;編譯器只看靜態型別決定方法是否存在。
任何需要 U 的地方都可以安全提供 T,則 T 是 U 的子型別。是:,子型別可傳遞。
的版本(執行期型別);動態分派。
不會。隱含呼叫 ,先建構父類別那部分;父類別沒有無參數建構子時必須明寫 。
前置條件不能更強、後置條件不能更弱、不變量保持、歷史約束。違反 2(永遠回 true)與 3(餘額可負)。
有 / 的可變矩形會違反(設寬 5 高 3 期望面積 15);不可變的 record 不違反。
讓測試彼此獨立、順序無關,一個測試留下的狀態不會影響另一個。
黑箱只透過公開介面依契約判斷,可套用所有實作;白箱依賴實作細節。(介面沒規定 null 行為)。
證明測試真的在檢查目標行為(否則可能是永遠通過的無效測試),並確認新功能確實尚未存在。
執行期;。編譯器只知道 是 ,轉型合法與否要到執行期才知道。
型別擦除:執行期不知道 是什麼。用 儲存,取出時 轉型並加 ,或直接用 。
(大多數 JVM):1000 超出 快取範圍(-128..127),兩個是不同物件;比較物件要用 。
(依鍵排序走訪);(平均 \(\bigO{1}\),不需順序)。
尊重等價(正確性)、確定性(正確性)、快、分布均勻(後兩者效能)。
算 找桶 → 沿鏈用 比對。若相等的鍵雜湊值不同,會找錯桶而找不到。
倍增:搬移總量 \(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 |