第四十五條、將局部變量的作用域最小化
將局部變量的作用最小化叫榕,可以增強(qiáng)代碼的可讀性和可維護(hù)性浑侥,并降低出錯(cuò)的可能性。異于C語言要求局部變量必須要在一個(gè)代碼塊的開頭處進(jìn)行聲明晰绎,Java允許在任何可以出現(xiàn)語句的地方聲明變量寓落。
最有力的方法是在第一次使用它的地方聲明。過早地聲明局部變量不僅會使它的作用域過早地?cái)U(kuò)展荞下,而且結(jié)束地過晚了伶选。局部變量的作用域從它被聲明的點(diǎn)開始拓展,一直到外圍塊的結(jié)束處尖昏。
幾乎每個(gè)局部變量的聲明都應(yīng)該包含一個(gè)初始表達(dá)式仰税。如果你還沒有足夠的信息來對一個(gè)變量進(jìn)行有意義的初始化,就應(yīng)該推遲這個(gè)聲明抽诉,直到可以初始化為止陨簇。(有個(gè)例外是try-catch語句有關(guān),變量被一個(gè)方法初始化迹淌,而這個(gè)方法可能會拋出一個(gè)checked exception河绽,則該變量必須在try的內(nèi)部被初始化。)
循環(huán)中提供了特殊的機(jī)會來將變量的作用域最小化唉窃。for循環(huán)都允許聲明循環(huán)變量(loop variable)耙饰,它的作用域被限定在正好需要的范圍內(nèi):循環(huán)體、循環(huán)體之前的初始化句携、測試榔幸、更新部分允乐。所以矮嫉,如果在循環(huán)終止后不再需要循環(huán)變量的內(nèi)容,for循環(huán)就優(yōu)先于while循環(huán)牍疏。for循環(huán)相較于while循環(huán)的另一個(gè)優(yōu)勢在于:更簡短蠢笋,從而增加了可讀性。
第四十六條鳞陨、for-each循環(huán)優(yōu)先于傳統(tǒng)的for循環(huán)
-
在Java1.5之前昨寞,對集合進(jìn)行遍歷的首選做法:
for(Iterator i = c.iterator();i.hasNext();){ doSomething((Element)i.next()); }
對數(shù)組進(jìn)行遍歷的首選做法:
for(int i = 0 ; i < a.length ; i++){ doSomething(a[i]); }
但是它們并不完美:迭代器和索引變量都會造成一些混亂瞻惋,也代表著出錯(cuò)的可能。
Java1.5 引入for-each循環(huán)援岩,通過完全隱藏迭代器或者索引變量歼狼,避免了混亂和出錯(cuò)的可能:for(Element e:elements){ doSomething(e); }
冒號代表在。享怀。羽峰。里面,可以讀作對于元素elements中的每個(gè)元素e添瓷。這個(gè)在性能上可能還稍有優(yōu)勢梅屉。
-
在對多個(gè)集合進(jìn)行嵌套式迭代的時(shí)候,for-each循環(huán)的優(yōu)勢更加明顯鳞贷。
for-each循環(huán)不僅讓你遍歷集合和數(shù)組坯汤,還讓你遍歷任何實(shí)現(xiàn)Iterable接口的對象。這個(gè)簡單的接口由單個(gè)方法組成:public interface Iterable<E>{ Iterator<E> iterator(); }
-
有三種情況下無法使用for-each循環(huán):
- 過濾:如果需要遍歷集合搀愧,并刪除選定的元素惰聂,就需要使用顯式的迭代器,以便可以調(diào)用它的remove方法咱筛;
- 轉(zhuǎn)換:如果需要遍歷列表或者數(shù)組庶近,并取代他部分或者全部的元素值,就需要列表迭代器或者數(shù)組索引眷蚓,以便設(shè)定元素的值鼻种;
- 平行迭代:如果需要并行地遍歷多個(gè)集合,就需要顯式地控制迭代器或者索引變量沙热,以便所有的迭代器或者索引變量都可以同步前移叉钥。
第四十七條、了解和使用類庫
通過使用標(biāo)準(zhǔn)類庫篙贸,可以充分利用這些編寫標(biāo)準(zhǔn)類庫的專家的知識投队,以及在你之前的其他人的使用經(jīng)驗(yàn)。而且不必浪費(fèi)時(shí)間為那些與工作不太相關(guān)的問題提供特別的解決方案爵川。而且它們的性能往往隨著時(shí)間的推移而不斷提高敷鸦。可以使自己的代碼融入主流寝贡。
每個(gè)Java程序員應(yīng)該熟悉java.lang扒披、java.util還有java.io中的內(nèi)容,關(guān)于其他類庫的知識可以隨時(shí)學(xué)習(xí)圃泡。
-
兩個(gè)工具:
- Collection Framework(集合框架)被加入到j(luò)ava.util包中碟案,是一個(gè)統(tǒng)一的體系結(jié)構(gòu),用來表示和操作集合颇蜡,允許它們對集合進(jìn)行獨(dú)立于表示細(xì)節(jié)的操作价说,從而減輕了編程的負(fù)擔(dān)且提高了效率和性能辆亏。
- java.util.concurrent 包中有一組并發(fā)實(shí)用工具,既包含高級的并發(fā)工具來簡化多線程的編程任務(wù)鳖目,還包含低級別的并發(fā)基本類型扮叨,允許專家們自己編寫更高級的并發(fā)抽象。
第四十八條领迈、如果需要精確的答案甫匹,請避免使用float和double
float和double類型主要是為了科學(xué)計(jì)算和工程計(jì)算而設(shè)計(jì)的。它們執(zhí)行二進(jìn)制浮點(diǎn)運(yùn)算惦费,這是為了在廣泛的數(shù)值范圍上提供較為精確的快速近似計(jì)算而精心設(shè)計(jì)的兵迅。然而,它們并沒有提供完全精確的結(jié)果薪贫。尤其不適用于貨幣計(jì)算恍箭,讓float和double精確地表示0.1是不可能的。
使用BigDecimal瞧省、int或者long進(jìn)行貨幣計(jì)算:如果想讓系統(tǒng)來記錄十進(jìn)制小數(shù)點(diǎn)扯夭,且不介意一絲不方便,請使用BigDecimal鞍匾;如果性能特別關(guān)鍵交洗,而且又不介意自己記錄十進(jìn)制的小數(shù)點(diǎn),涉及的數(shù)值不是很大橡淑,可以使用int和long构拳。如果數(shù)值超過18位數(shù)字,就必須使用BigDecimal梁棠。
第四十九條置森、基本類型優(yōu)先于裝箱基本類型
-
java有一個(gè)類型系統(tǒng)由兩個(gè)部分組成:
基本類型(primitive)如:int、double和boolean符糊;引用類型(reference type)如:String和List凫海。每個(gè)基本類型都有一個(gè)對應(yīng)的引用類型,稱作裝箱基本類型(boxed primitive)男娄。int/double/boolean對應(yīng)于Integer行贪、Double和Boolean。自動裝箱和自動拆箱模糊了但是沒有完全抹去基本類型和裝箱基本類型之間的區(qū)別模闲。
-
兩者之間的主要區(qū)別:
- 基本類型只有值建瘫,而裝箱基本類型則具有與它們的值不同的同一性。
- 基本類型只有功能完備的值围橡,裝箱基本類型還有個(gè)非功能值null暖混。
- 基本類型通常比裝箱基本類型更節(jié)省時(shí)間和空間缕贡。
對裝箱基本類型使用==操作符幾乎總是錯(cuò)誤的翁授;當(dāng)一項(xiàng)操作中混合使用基本類型和裝箱基本類型時(shí)拣播,裝箱基本類型就會自動拆箱,如果null對象引用被自動拆箱收擦,就會得到一個(gè)
NullPointerException
贮配。-
什么時(shí)候應(yīng)該使用裝箱基本類型呢?
- 作為集合中的元素塞赂、鍵和值泪勒,不能將基本類型放在集合中,因此必須使用裝箱基本類型宴猾;
- 在參數(shù)化類型中圆存,必須使用裝箱基本類型作為參數(shù)類型;
- 在反射的方法調(diào)用時(shí)仇哆,必須使用裝箱基本類型沦辙。
總結(jié):當(dāng)可以選擇的時(shí)候,基本類型優(yōu)先于裝箱基本類型讹剔∮脱叮基本類型更加簡單也更加快速,如果必須使用裝箱基本類型延欠,則要特別小心陌兑,自動裝箱減少了使用裝箱基本類型的繁瑣性,但是并沒有減少它的風(fēng)險(xiǎn)由捎。
第五十條兔综、如果使用其他類型更合適,則盡量避免使用字符串
字符串的優(yōu)點(diǎn):被用來表示文本狞玛,十分通用且Java支持的比較好邻奠。
-
不應(yīng)該使用字符串的情形:
- 字符串不適合代替其他的值,當(dāng)一段數(shù)據(jù)從文件为居、網(wǎng)絡(luò)或者鍵盤設(shè)備碌宴,進(jìn)入到程序中,通常是以字符串的形式存在蒙畴,如果它是數(shù)值贰镣,就應(yīng)該被轉(zhuǎn)換為適當(dāng)?shù)臄?shù)值類型等等;
- 字符串不適合替代枚舉類型:見第三十條
- 字符串不適合代替聚集類型:如果一個(gè)實(shí)體有多個(gè)組件膳凝,用字符串表示這個(gè)實(shí)體通常是很不恰當(dāng)?shù)谋。玫淖龇ó?dāng)然是編寫一個(gè)類來描述這個(gè)數(shù)據(jù)集,通常是一個(gè)私有的靜態(tài)成員類蹬音。
- 字符串不適合代替能力表(capabilities):有時(shí)候上煤,字符串被用于對某種功能進(jìn)行授權(quán)訪問,例如考慮設(shè)計(jì)一個(gè)提供線程局部變量機(jī)制著淆,這個(gè)機(jī)制提供的變量在每個(gè)線程中都有自己的值劫狠。幾年前面對這樣的設(shè)計(jì)任務(wù)時(shí)拴疤,有些人利用客戶提供的字符串鍵,對每個(gè)線程局部變量的內(nèi)容進(jìn)行訪問授權(quán)独泞。
總之:如果可以使用更加合適的數(shù)據(jù)類型呐矾,或者可以編寫更加適當(dāng)?shù)臄?shù)據(jù)類型,就應(yīng)該避免用字符串來表示對象。基本類型驻民、枚舉類型和聚集類型經(jīng)常被錯(cuò)誤地用字符串來代替材鹦。
第五十一條、當(dāng)心字符串連接的性能
+
(字符串連接操作符)是把多個(gè)字符串合并為一個(gè)字符串的便利途徑。要想產(chǎn)生單獨(dú)一行的輸出,或者構(gòu)造一個(gè)字符串來表示一個(gè)較小的、大小固定的對象毫炉,使用+是非常合適的。但是它不適合應(yīng)用在大規(guī)模的場景中削罩,為連接n個(gè)字符串而重復(fù)的使用字符串連接操作符瞄勾,需要n的平方級的時(shí)間。因?yàn)樽址豢勺兌鴮?dǎo)致的弥激,當(dāng)兩個(gè)字符串被連接在一起的時(shí)候进陡,它們的內(nèi)容都要被拷貝。-
為了獲得可以接受的性能微服,請使用StringBuilder替代String趾疚,兩種方法性能差別巨大,原先的做法開銷是隨著數(shù)量呈平方增加以蕴,改進(jìn)后是線性增加糙麦。
public String statement(){ StringBuilder b = new StringBuilder(numItems() *LINE_WIDTH); for(int i = 0; i< numItems();i++){ b.append(lineForItem(i)); } return b.toString(); }
原先的:
public String statement(){ String result = ""; for (int i = 0;i < numItems(); i++){ result += lineForItem(i); } return result; }
總結(jié):不要使用字符串連接操作符來合并多個(gè)字符串,除非性能無關(guān)緊要丛肮。相反赡磅,應(yīng)該使用StringBuilder的append方法。