六,避免使用終結(jié)(finalizer)函數(shù)
原因:終結(jié)函數(shù)通常是不可預(yù)測(cè)的,一般情況下不要使用,使用的結(jié)果會(huì)帶來(lái)很多問(wèn)題,不穩(wěn)定,性能差,移植問(wèn)題等等。
分析原因:
1,從一個(gè)對(duì)象不可到達(dá)到它的終結(jié)函數(shù)被執(zhí)行,這段時(shí)間是任意的,不確定的,所以時(shí)間關(guān)鍵的系統(tǒng)不應(yīng)該使用終結(jié)函數(shù)。
2,及時(shí)的執(zhí)行終結(jié)函數(shù)是垃圾回收算法決定的,這種算法在不同的JVM實(shí)現(xiàn)中會(huì)大相徑庭,使用終結(jié)函數(shù)的結(jié)果就是導(dǎo)致移植性問(wèn)題
3,如果執(zhí)行終結(jié)函數(shù)的線程一直低于當(dāng)前的線程的優(yōu)先級(jí),很可能造成占用大量?jī)?nèi)存,極端情況是出現(xiàn)OutOfMemoryError
4,JSL不保證終結(jié)函數(shù)一定被執(zhí)行,所以不要依賴終結(jié)函數(shù)來(lái)更新關(guān)鍵性的永久狀態(tài),例如數(shù)據(jù)庫(kù)的永久鎖
5,不要相信System.gc() System.runFinalization這兩個(gè)函數(shù),它們只能提高終結(jié)函數(shù)的執(zhí)行機(jī)會(huì),并不保證一定執(zhí)行。唯一保證一定執(zhí)行的是System.runFinalizersOnExit喝Runtime.runFinalizersONExit()但這兩個(gè)方法已經(jīng)被聲明不建議使用.
6,一個(gè)在終結(jié)函數(shù)中的一場(chǎng)不會(huì)打出任何信息
七:在改寫(xiě)equals方法的時(shí)候遵守通用約定
分析:
1,有些情況下不要隨意改寫(xiě)equals
(1),一個(gè)類的每個(gè)實(shí)例本質(zhì)上是唯一的,例如Thread
(2),不管新一個(gè)類是否提供了“邏輯相等”的測(cè)試功能,例如java.util.Random
(3),超類已經(jīng)改寫(xiě)了equals,從超類繼承過(guò)來(lái)的行為對(duì)于子類也是適合的 例如Set從AbstractSet繼承了equals
(4),一個(gè)類是私有的,或者是包級(jí)私有的,并且確定它的equals方法永遠(yuǎn)不會(huì)被調(diào)用
2, 通用的約定
自反性: 對(duì)于任意的引用值x ,x.equals(x)一定為true
對(duì)稱性: 對(duì)于任意的引用值x,y x.equals(y)返回true是 y.equals(x)也一定返回true
傳遞性:對(duì)于任意的引用值x,y,z 如果x.equals(y)返回true 并且y.equals(z)返回true 那么 x.equals(z)也一定是true
一致性:對(duì)于任意的x,y如果x,y沒(méi)有被更改,調(diào)用任意多次x.equals(y)返回的結(jié)果應(yīng)該一樣。
非空性:對(duì)任意的引用x ,x.equals(null)一定返回false
3不要將equals聲明中的Object對(duì)象換成別的類型
4,不要讓equals方法依賴不可靠資源
八:改寫(xiě)equals方法時(shí)總要改寫(xiě)hashCode
原因:來(lái)自java.lang.Object關(guān)于hashCode的規(guī)范
1,在一個(gè)應(yīng)用執(zhí)行期間,如果一個(gè)對(duì)象的equals方法比較所用到的信息沒(méi)有修改的話,那么對(duì)該對(duì)象調(diào)用hashCode多次,比如如一的返回同一個(gè)數(shù)
2,如果兩個(gè)對(duì)象的equals方法返回true,那么分別調(diào)用hashCode方法返回的值應(yīng)該相等
3,在兩個(gè)兌現(xiàn)的equals方法返回false時(shí),盡可能的讓hashCode方法返回的值不相等,提高散列表的性能
分析:如果改寫(xiě)了equals沒(méi)有改寫(xiě)hashCode在使用map等集合類的時(shí)候會(huì)出現(xiàn)問(wèn)題。
九:盡可能的改寫(xiě)toString方法,并在顯示內(nèi)容中盡可能的包括令人感興趣的信息。并且在注釋中表示出你的意圖。
十:謹(jǐn)慎的改寫(xiě)clone方法,改寫(xiě)前考慮淺拷貝和全拷貝
十一:考慮實(shí)現(xiàn)Comparable接口,如果你的對(duì)象要排序,那么記得實(shí)現(xiàn)這個(gè)方法
十二:使類和成員的可訪問(wèn)能力最小化,
十三:支持非可變性
非可變性遵循以下的原則:
1,不提供任何改變對(duì)象的方法
2,保證沒(méi)有可被子類改寫(xiě)的方法
3,保證所有的域都使final
4,使所有的域都成為私有的
5,保證任何可變組件互斥訪問(wèn)
6,非可變對(duì)象本質(zhì)是線程安全的,不需要同步
十四:復(fù)合優(yōu)于繼承(Think in java中有不少說(shuō)明)
十五:要們專門為繼承而設(shè)計(jì),并給出文檔說(shuō)明,要么禁止繼承
十六:接口優(yōu)于抽象類(參考一下GOF的設(shè)計(jì)模式)
十七:接口只是被定義類型,不要試圖使用常量接口
十八:優(yōu)先考慮靜態(tài)成員類
說(shuō)明:嵌套類有四種
靜態(tài)成員類 , 非靜態(tài)成員類, 匿名類, 局部類 除了第一種之外,其它三種都被稱為內(nèi)部類
1,靜態(tài)成員類是一種最簡(jiǎn)單的嵌套類,最好把它看成一個(gè)普通類,只是碰巧被聲明在另一個(gè)類內(nèi)部而已,
它可以訪問(wèn)外圍類的所有成員,包括那些生民為私有的成員。靜態(tài)成員類是外圍類的一個(gè)靜態(tài)成員,也遵守同樣的可訪問(wèn)性規(guī)則,如果它被聲明為私有的,那么它只能在外圍類內(nèi)部可以訪問(wèn)。靜態(tài)成員類的一個(gè)用法是公有的輔助類。例如HashMap的 static class Entry
非靜態(tài)成員類和靜態(tài)成員類的區(qū)別主要是非靜態(tài)成員類需要一個(gè)外圍類實(shí)例的引用,如果你不需要訪問(wèn)外圍類的實(shí)例的話,記得使用靜態(tài)成員類。
匿名類被使用的相對(duì)多一些,但是大量的使用匿名類會(huì)讓你的代碼比較亂,作過(guò)GUI開(kāi)發(fā)的人多會(huì)有所感觸。并且記住,盡可能的讓你的匿名類短小。
局部類,局部類的使用是最少的,很少會(huì)使用到這個(gè),如果用到記得使局部類盡可能的短小
對(duì)于C語(yǔ)言用戶的部分
十九:用類代替結(jié)構(gòu)
二十:用類層次代替聯(lián)合
二十一:用類來(lái)代替enum,但是在jdk1.5的時(shí)候提供了enum的支持,有些東西不一樣了
二十二:用類和接口代替函數(shù)指針
二十三、在函數(shù)的開(kāi)始檢查參數(shù)的有效性
如果函數(shù)對(duì)參數(shù)有要求,例如不接受Null ,不接受負(fù)數(shù)等等,應(yīng)該盡可能在函數(shù)的最開(kāi)始給出校驗(yàn),如果發(fā)現(xiàn)錯(cuò)誤拋出異常
二十四、在需要的時(shí)候使用保護(hù)性拷貝
1,假設(shè)類的客戶會(huì)盡一切手段來(lái)破壞這個(gè)類的約束條件,在這樣的前提下,你必須保護(hù)性的設(shè)計(jì)程序。
2,實(shí)例
注意,拷貝要在檢驗(yàn)之前進(jìn)行
3,參數(shù)類型可以被不可信任方子類化的情形,清不要使用clone方法進(jìn)行參數(shù)的保護(hù)化拷貝
二十五、謹(jǐn)慎的設(shè)計(jì)方法的原型
1,謹(jǐn)慎的選擇方法的名字,一個(gè)好的方法名字可以讓人很快記住
2,不要過(guò)于追求提供便利的方法,如果方法太多會(huì)增加使用者的學(xué)習(xí)負(fù)擔(dān),只有當(dāng)一個(gè)操作被頻繁使用的時(shí)候再添加一個(gè)對(duì)應(yīng)的方法。
3,避免太長(zhǎng)的參數(shù)列表,盡量讓你的參數(shù)不大于三個(gè)
4,對(duì)于參數(shù)類型,優(yōu)先使用接口,而不是類。
原因:如果使用接口,你可以隨意的替換實(shí)現(xiàn),或者同時(shí)存在多個(gè)實(shí)現(xiàn)。
使用類沒(méi)有這個(gè)優(yōu)勢(shì)。
5,謹(jǐn)慎的使用函數(shù)對(duì)象(一個(gè)類中一堆靜態(tài)函數(shù))
二十六、謹(jǐn)慎的使用重載
1,實(shí)例
結(jié)果是打印出三個(gè)unknown
這個(gè)程序的行為是違反直覺(jué)的,對(duì)弈重載方法的選擇是靜態(tài)的,而對(duì)于被改寫(xiě)的方法的選擇是動(dòng)態(tài)的
(這個(gè)可以參考我的另一篇文章)
2,盡量不要使用兩個(gè)參數(shù)數(shù)目相同的重載方法
二十七、使用零長(zhǎng)度數(shù)組代替Null作為返回值
原因:返回Null會(huì)造成使用者每次使用的時(shí)候都要作一次判斷,但有人會(huì)說(shuō)返回一個(gè)零長(zhǎng)度數(shù)組會(huì)產(chǎn)生new的開(kāi)銷,不如Null性能好。這個(gè)不是一定的,因?yàn)槲覀兛梢赃@樣來(lái)作
private final static Cheese[] NULL_CHESE_ARRAY = new Cheese[0];
每次需要的時(shí)候返回這個(gè)數(shù)組就好了。
二十八、為所有的導(dǎo)出Api元素編寫(xiě)文檔注釋
二十九、使一個(gè)局部變量的作用域最小化,最好的辦法使在第一次使用的時(shí)候聲明
1,幾乎每一個(gè)局部變量的聲明都應(yīng)該包含一個(gè)初始化表達(dá)式,如果你還沒(méi)有足夠的信息來(lái)初始化那就推遲聲明。
2,for循環(huán)優(yōu)先于while循環(huán),見(jiàn)下邊的例子
這個(gè)問(wèn)題源于復(fù)制粘貼,在編碼的過(guò)程中復(fù)制粘貼幾乎是不可避免的,使用for循環(huán)當(dāng)你出錯(cuò)的時(shí)候可以在編譯器發(fā)生錯(cuò)誤,而使用while則不會(huì)發(fā)現(xiàn)。盡早發(fā)現(xiàn)錯(cuò)誤總是好的。
三十、了解和使用庫(kù)(產(chǎn)生隨機(jī)數(shù))
詳細(xì):如果你希望產(chǎn)生一個(gè)位于0-某個(gè)上界的隨機(jī)數(shù),大多數(shù)的人的寫(xiě)法如下
這個(gè)方法存在三個(gè)缺點(diǎn):
缺點(diǎn)一:
如果n是一個(gè)比較小的2的乘方 那么經(jīng)過(guò)一段相當(dāng)短的周期后它產(chǎn)生的隨即數(shù)序列將會(huì)重復(fù)
缺點(diǎn)二:
如果n不是2的乘方,那么平均起來(lái)某些數(shù)比另外一些數(shù)出現(xiàn)的更為頻繁,如果n比較大則這個(gè)問(wèn)題更加顯著如果產(chǎn)生100萬(wàn)范圍內(nèi)的隨機(jī)數(shù),你會(huì)發(fā)現(xiàn)數(shù)字幾乎全部在0-666 666 ,前2/3的數(shù)字
缺點(diǎn)三:
在有些情況下會(huì)災(zāi)難性失敗,返回一個(gè)落在范圍之外的數(shù)字。原因是使用了Math.abs來(lái)得到一個(gè)非負(fù)數(shù)。
如果nextInt()返回 Integer.MIN_VALUE,那么abs后也會(huì)返回Integer.MIN_VALUE ,假設(shè)n不是2的乘方,那么取模操作符%將返回一個(gè)負(fù)數(shù),這幾乎肯定造成你的程序失敗,而且這個(gè)失敗很難重現(xiàn)。
為了編寫(xiě)一個(gè)避免上邊三個(gè)缺點(diǎn)的random,你必須了解線性同于偽隨機(jī)發(fā)生器、數(shù)論、和2的求補(bǔ)運(yùn)算知識(shí)。不過(guò)Jdk已經(jīng)實(shí)現(xiàn)了一個(gè)現(xiàn)成的可以使用,那就是Random.nextInt(int)
這一段很多比較簡(jiǎn)單,簡(jiǎn)單羅列一下,部分重要的做了解釋
三十一、如果要求精確的答案,盡量避免使用float 和double,這個(gè)可以參照我的一片文章
貨幣尤其不合適??梢允褂肂igDecimal代替
三十二、如果其它類型更適合,盡量避免使用字符串
1,字符串不能替代其它的值類型
2,字符串不適合代替枚舉類型
3,字符串不適合代替聚集類型
4,字符串也不是和代替能力表
因?yàn)橛行r(shí)候,使用字符串會(huì)大大降低性能
三十三、了解字符串連接的性能
說(shuō)明:使用StringBuffer代替 +來(lái)連接字符串
三十四、通過(guò)接口來(lái)引用對(duì)象,這能讓你的程序更加靈活
三十五、接口優(yōu)先于反射。
使用反射會(huì)帶來(lái)很多問(wèn)題,例如:
1,不能編譯期發(fā)現(xiàn)錯(cuò)誤
2,代碼混亂
3,調(diào)試?yán)щy
4,性能損失。
除非必須,否則不使用反射
三十六、謹(jǐn)慎的使用本地方法JNI
三十七、謹(jǐn)慎的進(jìn)行優(yōu)化,有三條優(yōu)化格言:
1,很多計(jì)算上的過(guò)失都被歸咎于效率原因(沒(méi)有獲得必要的效率),而不是其它的原因--甚至包括盲目的作傻事. ---William A.Wulf [Wulf72]
2,不要去計(jì)較一些小的效率上的得失,在97%的情況下,不成熟的優(yōu)化是一切問(wèn)題的根源。
------Donald E.Knuth[Knuth74]
3,在優(yōu)化方面要遵守兩個(gè)原則:
規(guī)則一:不要做優(yōu)化
規(guī)則二:還是不要做優(yōu)化--也就是說(shuō),在你還沒(méi)有絕對(duì)清晰的未優(yōu)化方案前,請(qǐng)不要優(yōu)化。
-----M.A.Jackson[Jackson75]
每次試圖做優(yōu)化之前和之后請(qǐng)對(duì)性能進(jìn)行測(cè)試
三十八:遵守普遍接受的命名規(guī)則
三十九:值針對(duì)不正常的條件才使用異常,也就是說(shuō)不要在正常的情況下使用異常來(lái)控制流程,活著解決某些已知的問(wèn)題。因?yàn)闀?huì)大量的損失性能
四十、對(duì)于可恢復(fù)的條件使用被檢查的異常,對(duì)于程序錯(cuò)誤使用運(yùn)行時(shí)異常
詳細(xì):Java提供了三種可拋出結(jié)構(gòu),checked Exception, run-time exception , error
什么時(shí)候使用什么很容易讓人混淆,下邊是一個(gè)簡(jiǎn)單的區(qū)分原則
1,如果期望調(diào)用者能夠恢復(fù),那么對(duì)于這樣的條件應(yīng)該使用被檢查異常
2,你所實(shí)現(xiàn)的所有未檢查的拋出結(jié)構(gòu)都是run time exception ,而不是Error
四十一:避免不必要的使用被檢查異常
四十二:盡可能的使用標(biāo)準(zhǔn)異常,例如IllegalArgumentException ,NullPointerException ,IndexOutOfBoundsException等等
四十三:拋出異常要適合于相應(yīng)的抽象。
高層實(shí)現(xiàn)應(yīng)該捕獲異常,同時(shí)拋出一個(gè)可以按照高層抽象解釋的異常(業(yè)務(wù)邏輯上符合高層邏輯),這種做法叫做異常轉(zhuǎn)譯
四十四:每個(gè)方法拋出的異常都應(yīng)改有文檔
四十五:在細(xì)節(jié)消息中包含失敗-捕獲信息
詳細(xì):在異常字符串中應(yīng)包含有用的信息,例如IndexOutOfBoundsException異常的細(xì)節(jié)消息應(yīng)該包括下界、上界以及沒(méi)有落在其中的實(shí)際下標(biāo)
聯(lián)系客服