小型函式庫的契約往往比 API 表面更能決定正確性。以下規則可避免常見的 Java 整合失敗。
物件契約 #
equals 必須具備反身性、對稱性、傳遞性與一致性,並且對null回傳 false。
如果兩個物件相等,它們必須具有相同的hashCode;因此,在將值作為雜湊鍵之前,
應同時覆寫這兩個方法。物件作為映射鍵期間,不要變更參與相等性判定的欄位。
如果繼承會破壞對稱性,應優先使用組合而非繼承。
Object.clone會執行欄位複製,且與建構函式、繼承和可變欄位的配合不佳。
應優先使用複製建構函式或命名為copyOf的工廠方法。陣列是範圍有限的例外,
因為其clone具有可預測的淺陣列契約。
文字、正規表示式與字串 #
對於會重複使用的Pattern,應先進行編譯。Matcher.matches()要求整個輸入都符合,
而find()會搜尋下一個符合的區域。Pattern.quote(value)會跳脫放入正規表示式的
字面文字;Matcher.quoteReplacement(value)會跳脫替代字串。兩者不可互換。
自 JDK 9 起,HotSpot 的String實作使用緊湊字串:透過byte[]加上編碼標記,
可以緊湊地表示 Latin-1 文字,其他情況則使用 UTF-16。這是實作細節;Java
String仍然是 UTF-16 程式碼單位的序列,且必須在 I/O 邊界明確進行Charset轉換。
計數器與列舉集合 #
AtomicLong提供一個精確且線性一致的值。LongAdder會在競爭下將更新分散到
多個儲存格,因此更新具有更好的擴充性,但與更新同時執行的sum()不是原子快照。
計量指標使用LongAdder;當每個觀測到的總數都會驅動正確性判定時,使用
AtomicLong。
Enum<E extends Enum<E>>為單一列舉型別提供型別安全的排序與操作。EnumSet與
EnumMap利用這個封閉的鍵空間,並保留列舉宣告順序;對於列舉網域,應優先使用
它們,而不是HashSet和HashMap。
集合與執行緒 #
Thread.State會回報BLOCKED、WAITING和TIMED_WAITING等 JVM 層級狀態;
它是觀察機制,而非同步協定。應使用join、future、閂鎖或結構化擁有權來協調執行緒。
Map是否允許null鍵或值取決於實作:HashMap允許一個 null 鍵,而
ConcurrentHashMap拒絕 null 鍵和值;在自然排序下,TreeMap也可能拒絕 null
鍵。不要假設Map介面的選用操作一定成功,而應驗證所選實作的契約。
Arrays.asList(array)會回傳由提供的物件陣列支援的固定大小清單。set會更新
該陣列,但add和remove會失敗。傳入int[]會建立包含一個元素的List<int[]>,
因為可變參數不會將基本型別陣列裝箱。需要裝箱值清單時,請使用
Arrays.stream(ints).boxed().toList()。
評論
在 GitHub 上閱讀或評論(發表評論需要登入)