標籤:java解惑 缺陷與陷阱
A1 詞彙問題A 1.1 字母l在許多字型中都與數字1相像
規則:在long類型字面常量中,應該總是用大寫L,千萬不要用小寫l。不要用孤零零的一個l作為變數名。 謎題4
A 1.2 負的十六進位字面常量看起來像正的
規則: 要避免混合類型的計算。在恰當的地方要用long類型的字面常量代替int類型的字面常量。謎題5
A 1.3 八進位字面常量與十進位字面常量相像
規則: 要避免八進位字面常量。如果非得使用它們,那麼請對所有用到的地方進行注釋,以使得你的意圖清晰。 謎題59
A 1.4 ASCII字元的Unicode逸出字元容易令人迷茫
規則: 不要使用ASCII字元的Unicode逸出字元。如果可以的話,應該直接使用ASCII字元。在字串字面常量和字元字面常量中,應該優選逸出字元列而不是Unicode逸出字元。 謎題14、16、17
A 1.5 反斜線必須被轉義,即使是在注釋中
規則: 如果你在編寫一個可以產生Java原始碼的系統,在產生的字元字面常量、字串字面常量和注釋中都要對反斜線進行轉義。Windows檔案名稱最容易引發問題。 謎題15、16
A 1.6 塊注釋不要嵌套
規則使用單行注釋來注釋掉代碼 謎題19
A 2 整數運算A 2.1 %操作符的非零結果具有和左運算元相同的加號或減號
規則: 如果你需要一個非負的餘數,而%操作符的結果又是負的,那麼你應該在結果上加一個模數。 謎題1、64
A 2.2 整數運算的悄悄溢出
規則: 要用足夠大的類型來儲存結果,包括中間結果。 謎題3、26、33、65
A 2.3 int數值之差的加號或減號不能可靠地指示其大小順序
規則: 不要用基於減法的比較子,除非你可以肯定數值的差不會大於Ingeter.MAX_VALUE。 謎題65
A 2.4 複合賦值操作符可能造成悄悄的窄化轉型
規則: 不要在類型為byte、short或char的變數上使用複合操作符。 謎題9、31
A 2.5 整數類型不對稱: Integer.MIN_VALUE是它自己的負值,Long.MIN_VALUE也一樣
規則: 要保守地編程,如果有必要就用long來代替int。 謎題33、64
A 2.6 移位操作符只用了其右運算元的低位
規則: 移位的位元應該用常量。如果移位的位元必須用變數,那麼一定要檢查移位的距離是否在允許的範圍內。 謎題27
A 2.7 當在整數類型之間轉換時,如果源類型是有符號的則執行符號擴充
規則: 操作byte類型的數值時一定要小心,它是有符號的。要想禁止符號擴充,可以使用位元遮罩。 謎題6
A 3 浮點運算A 3.1 浮點運算是不精確的
規則: 在需要精確結果的地方不要使用浮點,應該使用一個整數類型或BigDecimal。
要避免浮點類型的迴圈索引。
要避免在浮點變數上使用++和—操作符,因為這些操作對多數的浮點數都不起任何作用。
避免測試浮點值是否相等。
寧願用double,而不用float。 謎題2、28、34
A 3.2 NaN不等於任何浮點數值,也包括它自身
規則: 要避免測試浮點數的相等性。這麼做並不總能避免問題,但至少是一個良好的開端。 謎題29
A 3.3 從int到float、從long到float以及從long到double的轉換是有損精度的
規則: 要避免整型和浮點型混合的計算。要優選整型算術而不是浮點型算術。 謎題34、 87
A 3.4 BigDecimal(double)構造器返回的是其浮點型參數的精確值
規則: 應該總是使用BigDecimal(String)構造器,而永遠不要使用BigDecimal(double)。 謎題2
A 4 運算式計算A 4.1 混合類型計算容易令人迷茫
規則: 要避免混合類型的計算。
當把?:操作符作用於數字運算元時,第二個和第三個運算元要使用相同的數字類型。寧願使用不變的變數,不願使用內建的幻數。 謎題5、8、24
A 4.2 操作符的運算元是從左至右計算的
規則: 要避免在同一個運算式中對相同的變數多次賦值。尤其是在同一個運算式中的多重複合賦值操作符特別令人迷茫 謎題7、25、42
A 4.3 操作符的優先順序並不總是很明顯
規則: 要使用括弧而不是空格來讓優先順序變得明顯。要用具名的常量變數來替換內聯的常量運算式。 謎題11、 35、 95
A 4.4 操作符==和!= 在被封裝的基本類型上執行引用比較
規則: 要想強制進行值比較,需要在比較之前將一個運算元賦值或轉型成恰當的基本類型。 謎題32
A 4.5 常量變數在所用的地方是內聯的
規則: 要避免匯出常量欄位,除非它們表示的是永遠都不會變化的真正的常量。可以使用一個恒等函數將一個運算式變成非常量。謎題93
A 4.6 操作符&和|即使在作用於布爾類型的數值時,也要同時計算其兩個運算元
規則: 要避免將&和|作用與布爾類型的運算元。對有意識的使用要加以注釋。謎題42
A 5 控制流程A 5.1 在switch case語句中缺少break將導致控制流程一貫而下
規則: 不要從一個非空的case一貫而下到另一個case:要用一個break終止每一個非空的case。對任何有意識的一貫而下要加以注釋 。 謎題23
A 5.2 在Integer.MAX_VALUE上終止以int為索引的迴圈是困難的
規則: 要在Integer.MAX_VALUE處終止一個以int為索引的迴圈,需要使用long類型的迴圈索引,或者要非常仔細小心地編寫該迴圈。 謎題26
A 5.3 finally語句塊的意外完成將掩飾掛起的控制傳輸
規則: 要確保每一個finally語句塊都正常完成,以防止致命的錯誤。不要從一個finally語句塊中返回或拋出一個異常。 謎題36、41
A 5.4 為正常的控制流程使用異常將導致bug和很差的效能
規則: 應該只為異常情況使用異常,永遠不要為正常的控制流程使用異常。謎題42
A 6 類初始化A.6.1 類初始化是自頂向下的
規則:要確保靜態欄位以恰當的順序被初始化。要使用延時初始化來解決初始化迴圈問題,該問題可以涉及一個或多個類。 謎題49 52
A.6.2 oNoClassDefFoundError出現的時機是不可靠的
規則:不要捕獲NoClassDefFoundError, 而是應該使用反射並捕獲ClassNotFoundException。更一般地講,不要捕獲Error及其子類。 謎題 44
A.7 執行個體的建立與銷毀A.7.1 執行個體初始器在構造器方法體之前執行
規則:如果自身類型的執行個體欄位在構造階段會引發遞迴,那麼要確保該遞迴能夠終止。 謎題40
A.7.2 在構造器中調用被覆蓋的方法會導致該方法在執行個體初始化之前運行
規則:永遠不要在構造器中調用可覆蓋的方法。要用延遲初始化來解決初始化迴圈問題。謎題51
A.7.3 引用無效失敗會導致記憶體泄露
規則: 要能夠使長生命週期的對象中已淘汰的對象引用變成無效。不能做到這一點就會導致記憶體泄露,在諸如java這種記憶體回收語言中,更恰當的說法是無意識的對象保留。
A.7.4 添加私人構造器失敗會使類可執行個體化
規則:如果你想讓一個類不可執行個體化,那麼就添加一個私人構造器。更一般地講,應該總是至少提供一個構造器,永遠都不要依賴於預設的構造器。
A.7.5 終結器是不可預知的、危險的,且速度很慢
規則: 要避免使用終結器。
A.7.6 被複製的對象可以共用內部狀態
規則: 要避免實現Cloneable介面。如果實現了它,那麼你應該複製所有不想在該對象及其複製之間共用的內部對象。
A.8 其他與類和執行個體相關的主題A.8.1 在靜態方法上沒有任何動態指派
規則: 永遠不要用運算式來限定靜態方法調用,應該總是用類型來限定。謎題48
A.8.2 內部類是令人迷茫的
規則: 優先考慮使用靜態成員類而不是內部類。 謎題80 89 92
A. 8.3 不能做保護複製就會破壞不變性
規則: 在需要的時候,要對輸入參數和輸出值做保護複製。
A.8.4 實現一個介面會影響實作類別的API
規則:不要去實現一個可以擷取對其靜態欄位的無限定訪問權的介面。不要編寫只是由欄位組成的介面,即所謂的常量介面。在5.0及以後,可以使用靜態匯入作為靜態介面這一有害模式的替代物。
A.8.5 int常量作為枚舉值不安全
規則: 應該使用enum類型,或者,如果你使用5.0之前的版本,應該實作類別型安全的枚舉。
A.8.6 混合使用基本類型和參數化型別將弱化類型檢查機制
規則: 應該在你的代碼中消除被報告的“不受檢查”的警告。在5.0及以後,避免使用基本類型。 謎題88
A. 8.7 返回null而不是0長度的數組或集合有產生錯誤的傾向
規則:不要從一個返回數組或集合的方法中返回null。
A.9 名字重用A.9.1 想要覆蓋時很容易就變成了重載
規則:請機械地複製每一個你想要覆蓋的超類方法的聲明,但最好還是讓IDE幫忙做。如果使用5.0,請使用@Override注釋。 謎題58
A.9.2 重載解析規則不明顯
規則: 要避免重載。如果你在API中的兩個方法都可以應用於某些調用,那麼請確保這兩個方法在相同的實參上具有相同的行為。 謎題11 46 74
A.9.3 隱藏實體的程式難以理解
規則: 要避免隱藏 謎題66 72 92
A.9.4 遮蔽實體的程式難以理解
規則:要避免遮蔽。不要在公用API中重用java.lang.Object中的名字。不要試圖在一個已經被定義的名字上使用靜態匯入。 謎題71 73 89
A.9.5 遮掩實體的程式難以理解
規則: 要避免遮掩。要遵守命名規範。 謎題68 69
A.9.6 與所在類具有相同名字的方法看似構造器
規則: 要遵守命名規範。 謎題63
A.9.7 重用平台類名的程式難以理解
規則: 要避免重用平台類的名字,並且永遠不要重用java.lang中的類名。 謎題67
A.10 字串A.10.1 數組不能覆蓋Object.toString
規則: 對於char數組,應該使用String.valueOf來擷取表示指定字元序列的字串。對於其他類型的數組,應該使用Arrays.toString,如果是5.0以前,應該使用Arrays.asList。 謎題12
A.10.2 String.replaceAll以Regex作為第一個參數
規則: 要確保該參數是一個合法的Regex,要不然就用String.replace來代替。 謎題20
A.10.3 String.replaceAll以置換字串作為第二個參數
規則: 要確保該參數是一個合法的置換字串,要不然就用String.replace來代替。 謎題20
A.10.4 重複地進行字串串連可能導致極差的效能
規則: 要避免在迴圈中使用字串串連。
A.10.5 從位元組數組到字元數組的轉換需要指定字元集
規則:在將一個byte數群組轉換成一個字串或一個char數組時,總是要選擇一個字元集; 如果你沒有這麼做,就會使用平台預設的字元集,從而導致不可預知的行為。 謎題18
A.10.6 char類型值只會預設轉換從int,而不是String
規則: 要想把一個char轉換成一個字串,應該使用String.valueOf(char)。 謎題11 23
A 11 I/O A.11.1 Stream.close可以拋出IOException異常
規則: 要在close上捕獲異常,並且一般做法是忽略這些異常。 謎題41
A.11.2 PrintStream.write(int)不重新整理輸出資料流
規則: 要避免使用PrintStream.write(int)。如果你使用了它,要在需要時調用flush。 謎題81
A.11.3 要消費掉一個進程的輸出,否則該進程可能掛起
規則: 應該總是消費掉你所建立的進程的輸出。 謎題82
A 12 線程A.12.1 調用Thread.run不能啟動一個線程。
規則: 永遠不要調用Thread.run。 謎題76
A.12.2 庫類可能鎖住或通知它們的執行個體
規則:如果你在擴充一個庫類,那麼請不要使用執行個體鎖。取而代之的是,應該使用儲存在一個私人欄位中的單獨的鎖對象。 謎題77
A,12,3 Thread.interrupted會清除中斷狀態
規則: 不要使用Thread.interrupted,除非你想要清除當前線程的中斷狀態。 謎題84
A.12.4 類初始化過程中將持有該類的鎖
規則: 要避免死結的風險,永遠不要在類初始化過程中等待一個後台線程。 謎題85
A.12.5 在共用可變狀態時同步失敗,可能導致不能觀察狀態的變化
規則: 要同步對共用的可變狀態的訪問。
A.12.6 在被同步的語句塊中調用外部方法可能導致死結
規則: 永遠不要將控制流程讓給在被同步的方法或語句塊中調用的外部方法。
A.12.7 在while迴圈的外部調用wait方法會引發不可預知的行為
規則: 永遠不要在一個while迴圈的外部去調用wait。
A.12.8 對線程調度器的依賴可能導致不定的且平台依賴的行為
規則: 為了編寫健壯的、相應迅速的和可移植的多線程程式,應該確保在任何給定的時刻,都只有極少數的線程是可啟動並執行。
A 13 反射A.13.1 反射將檢查對實體和實體所屬類的存取權限
規則: 用反射執行個體化類,用介面訪問執行個體。 謎題78
A.13.2 用反射執行個體化內部類需要一個額外的參數
規則: 不要在內部類上使用反射。優先考慮使用靜態成員類,而不是內部類。 謎題80
A.13.3 Class.newInstance可以拋出未聲明的受檢查異常
規則: 只要存在構造器會拋出受檢查異常的任何可能性,那麼就應該使用java.lang.reflact.Constructor.newInstance而不是Class.newInstance。 謎題43
A 14 序列化A.14.1 讓一個類可序列化將引入一個公用的偽構造器
規則: 在讓一個類可序列化之前,一定要三思而行。在接受預設的readObject方法之前,一定要三思而行。編寫readObject方法時,要採取保護性措施。
A.14.2 序列化形式是類的公用API的一部分
規則: 在設計序列化形式時,應該與設計任何其他的API一樣小心仔細。
A.14.3 使用預設的序列化形式會在類的公用API中泄露私人欄位
規則: 應該考慮使用某種定製的序列化形式
A.14.4 使用預設的序列化形式可能導致效能低下
規則: 應該考慮使用某種定製的序列化形式。
A.14.5 維護執行個體控制的不變規則需要一個readResolve方法
規則: 應該總是為單例、自編的型別安全的枚舉類型以及其他執行個體控制的可執行個體化的類編寫一個readResolve方法。 謎題83
A.14.6 聲明序列版本UID失敗會導致脆弱。
規則: 應該在可序列化的類中聲明一個顯式的序列版本UID。
A.14.7 如果readObject或readResolve調用了可覆蓋的方法,還原序列化迴圈的對象可能引發崩潰
規則: 如果一個HashSet、HashMap或Hashtable將被序列化,那麼要確保其內容不會返回來引用到它。在readObject和readResolve方法中,要避免在當前正在被序列化的對象上調用方法,如果你無法聽從這條建議,那麼也要確保在對象圖中不會存在任何有問題的迴圈。 謎題91
A 15 其他庫A.15.1 覆蓋equals方法而不覆蓋hashCode方法可能會引發不定的行為
規則: 當覆蓋equals方法時,總是要一併覆蓋hashCode方法 謎題57
A.15.2 Calendar和Date設計得很差勁
規則: 在使用Calendar和Date時,一定要參考API文檔。
A.15.3 許多類不管其方法名是什麼,這些類都是不可變的
規則: 不要被誤導而認為不可變的類型是可變的,不可變的類型包括String、Integer,Long,Short, Byte, Character, Boolean, Float, Double, BigInteger, BigDecimal. 謎題56
A.15.4 某些被棄用的方法對程式來說是毒藥
規則: 要避免使用已經被棄用的方法,例如Thread.stop,Thread.suspend, oRuntime.runFianlizersnExit和System.runFianlizerOnExit。 謎題39 43
A.15. 5 使用自編的解決方案而不是庫容易導致努力白費、bug產生以及極差的效能
規則: 要瞭解並使用庫 謎題60 62 94.
java解惑之陷阱和缺陷的目錄總結