第二章:創建和銷毀對象
第7條:避免使用終結方法
final、finally、finalize的區別:
final:修飾符,關鍵字,可以修飾成員、方法和類,如果類被聲明為final,意味著它不能再派生出新的子類,不能作為父類被繼承。將變量或者方法聲明為final,可以保證他們在使用中不被改變。
finally:一般是用于異常處理中,提供finally塊來執行任何的清楚操作,try{} catch(){} finally{}。
finalize:用于垃圾回收,在java中,允許使用finalize()方法在垃圾收集器將對象從內存中清理出去之前做必要的清理工作。
第三章對所有對象都通用的方法
第8條:覆蓋equals時請遵守通用約定
- 使用==操作符檢查“參數是否為這個對象的引用”。
- 使用instanceof操作符檢查“參數是否為正確的類型”。
- 把參數轉換成正確的類型。
- 對于該類中的每個“關鍵”域,檢查參數中的域是否與該對象中對應域相匹配。
- 當覆蓋玩equals方法之后,應該問自己三個問題:它是否對稱的、傳遞的、一致的?
warning:
- 覆蓋equals時總要覆蓋hashCode;
- 不要企圖讓equals方法過于智能;
- 不要將equals聲明中的Object對象替換為其他的類型;
public boolean equals(Myclass myclass){
.....
}
第9條:覆蓋equals時總要覆蓋hashCode
每個覆蓋了equals方法的類中,也必須改服hashCode方法;
相等的對象必須具有相等的散列碼;
為不相等的對象產生不相等的散列碼;
不要試圖從散列碼計算中排除一個對象的關鍵部分來提高性能;
第10條:始終要覆蓋toString()
java.lang.Object提供了一個toString()方法,其結果為類名+@+hashCode十六位無符號表示方法。覆蓋toString()方法,獲取簡潔的類toString()結果,便于程序中類輸入結果的閱讀。
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
第11條:謹慎地覆蓋clone
如果你覆蓋了非final類中的clone方法,則應該返回一個通過調用super.clone而得到的對象;
clone方法就是另一個構造器,你必須確保不會傷害到原始的對象,并確保正確地創建克隆對象中的約束條件;
第12條:考慮實現Complarable接口
Object中并沒有聲明compareTo()方法,compartTo()是Comparable接口的唯一方法,繼承此接口的類,可以實現類內部排序;
public interface Comparable{
public int compareTo(T o);
}
其返回結果為int型,當對象小于、等于、大于指定對象T時,返回結果分別為負整數、零和正整數;
第4章:類與接口
第13條:使類和成員的可訪問性最小化
盡可能地使每個類或者成員不被外界訪問;
可訪問性:
- 私有的private;
- 包級私有的package-private,聲明該成員包內部的任何類都可以訪問這個成員,不對成員做任何訪問級別限制,則默認為包級私有;
- 受保護的protected,類及其子類可訪問protected級別成員;
- 公開的public,在任何地方都可以訪問該成員;
第14條:在公有類中使用訪問方法而非公有域
公有類永遠不要暴露可變的域。雖然這還是有問題,但是讓公有類暴露不可變的域其危害比較小。
第15條:使可變性最小化
為了使類成為不可變,要遵循下面五條規則:
- 不要提供任何會修改對象狀態的方法;
- 保證類不會被擴展;
- 使所有的域都是final的;
- 使所有的域都成為私有的;
- 確保對于任何可變組件的互斥訪問;
第16條:復合優于繼承
只有子類真正是超類的一部分時,才可以使用繼承,即當子類只有包含超類的屬性時才能使用繼承;
繼承的功能非常強大,但是由于繼承違背了封裝原則,因此也存在諸多問題。可以用復合和轉發機制來代替繼承,尤其是當存在適當的接口實現包裝類時,包裝類比子類更加健壯,功能也更強大;
第17條:要么為繼承而設計,并提交文檔說明,要么就禁止繼承
第18條:接口優于抽象類
第19條:接口只用于定義類型
接口只應用于定義類型,而不應該被用來到處常量;
第20條:類層次優于標簽類
標簽類過于冗長,容易出錯,并且效率低下;
第21條:用函數對象表示策略
第22條:優先考慮使用靜態成員類
嵌套類:是指被定義在另一個類的內部的類;
嵌套類分類:靜態成員類、非靜態成員類、匿名類、局部類;
第6章 枚舉和接口
第30條:用enum代替int常量
枚舉類型:是指由一組固定的常量組合成合法值得類型,例如一年的春夏秋冬四個季節;
為了將數據與枚舉常量關聯起來,得聲明實例域,并編寫一個帶有數據并將數據保存在域中的構造器。
第31條:用實例域代替序數
永遠不要根據枚舉的序數導出與它關聯的值,而是將它保存在一個實例域中。
第32條:用EnumSet代替位域
用OR位運算將幾個常量合并到一個集合中,稱作位域;
EnumSet類可以有效地從單個枚舉類型中提取多個枚舉值;
eg:
public enum Style{
BOLD,
ITALIC,
UNDERLINE
}
Set<Style> styleSet = EnumSet.of(Style.BOLD,Style.ITALIC);
由于枚舉類型要用在集合set中,所以沒有理由用位域表示它。
第33條:用EnumMap代替序數索引
EnumMap繼承AbstractMap實現Map接口,其實現Map所有操作的同時,具有獲取鍵列表和值列表功能;
getKeyUniverse()返回鍵列表,values()返回值列表;
第34條:用接口模擬可伸縮的枚舉
雖然無法編寫可擴展的枚舉類型,卻可以通過編寫接口以及實現該接口的基礎枚舉類型,對它進行模擬;
第35條:注解優先于命名模式
一般使用命名模式,表明有些程序需要通過某種工具或者框架進行特殊處理;
java 元注解:
1.@Target,
2.@Retention,
3.@Documented,
4.@Inherited
有了注解,就不需要再使用命名模式了;
第36條:堅持使用override注解
使用@Override注解,表示該方法覆蓋超類中的方法;此注解保留源碼級別,當java文件被編譯后,@Override會被去除;使用此注解便于程序員對程序的理解,此外編譯器會根據此注解對無意識的覆蓋給予提示;
第37條:用標記接口定義類型
標記接口:是沒有包含方法聲明的接口,例如:Serializable就是一個標記接口,通過實現此接口,表明類可以被序列化。
第7章 方法
第38條:檢查參數的有效性
對參數的任何限制都是件好事。再設計方法時,應該使它們盡可能的通用,并符合實際需要。
每當編寫方式或者構造器的時候,應該考慮它的參數有哪些限制。應該把這些限制寫在文檔中,并且在這個方法體的開頭處,通過顯式的檢查來實施這些限制。
第39條:必要時進行保護性拷貝
- 類的客戶端會盡其所能來破壞這個類的約束條件,因此你必須保護性地設計程序。
- 保護性拷貝是在檢查參數的有效性之前進行的,并且有效性檢查是針對拷貝之后的對象,而不是針對原始對象。
- 對于參數類型可以被不可信任方子類話的參數,請不要使用clone方法進行保護性拷貝。
保護性拷貝示例
public Date start(){
return new Date(start.getTime());
}
第40條:謹慎設計方法簽名
- 謹慎的選擇方法名稱,應該始終遵循標準的命名習慣;
- 不要過于追求提供便利的方法;
- 避免過長的參數列表,可以通過輔助類縮短參數列表;
第41條:慎用重載
永遠不要導出兩個具有相同參數目的的重載方法
第42條:慎用可變參數
在定義參數數目不定的方法時,可變參數方法時不種很方便的方法,但是它們不應該過度濫用。如果使用不當,會產生混亂的結果。
第43條:返回零長度或者集合,而不是null
返回類型為數組或集合的方法沒理由返回null,應該返回零長度的數組或集合;
第44條:為所有導出的API元素編寫文檔注釋
第八章 通過程序設計
第45條:將局部變量作用域最小化
將局部變量最小化,可以增強代碼的可讀性和可維護性,并降低錯的可能性。
- 要使局部變量的作用域最小化,最有力的方法就是在第一次使用它的時候聲明;
第46條:for-each循環優先于傳統的for循環
java1.5發行版本中引入for-each循環,通過完全隱藏迭代器或者索引變量,便面了混亂和出錯的可能。eg:
for(Element e : elements){
doSomeThing(e);
}
有三種情況無法使用for-each循環:
- 過濾--如果需要遍歷集合或者數組,并刪除指定的元素;
- 轉換--如果需要遍歷列表或者數組,并取代它不分或者全部的元素值;
- 平行迭代--如果需要并行地遍歷多個集合,就需要顯式地控制迭代器或者索引變量,以便所有迭代器或者索引量都可以得到同步前移;
第47條:了解和使用類庫
java.io java.lang java.util java.util.concurrent類庫是每個程序員都應該學習的;
多了解類庫,多閱讀類庫實現原理及實現方式,類庫的代碼比你自己編寫的代碼更好一些,并隨著時間推移而不斷改進。
第48條:如果需要精確的答案,避免私用floate和double
使用BigDecimal代替floate和double
第49條:基本類型有限裝箱基本類型
基本類型與基本裝箱類型的區別:
- 基本類型只有值,裝箱基本類型則具有它們的值不同的同一性;
- 基本類型只有功能完備的值,而每個裝箱基本類型除了它對基本類型的所有功能值之外,還有個非功能值:null
- 基本類型比裝箱基本類型更節約時間和空間
第50條:如果其他類型更合適,避免使用字符串
字符串不適合代替其他值類型
字符串不適合代替枚舉類型
字符串不適合代替能力表
如果可以使用更加合適的數據類型,或者可以編寫更加適當的數據類型,就應該避免使用字符串
第51條:當心字符串連接的性能
為了獲取能夠接受的性能,請使用StringBuilder代替String
第52條:通過接口引用對象
如果有合適的接口類型存在,那么對于參數、返回值、變量和域來說,就都應該使用接口類型進行聲明;
第53條:接口優先于反射機制
核心反射機制:提供了“通過程序來訪問關于已裝載的類的信息”的能力;
第54條:謹慎地使用本地方法
本地方法是指用本地程序設計語言(c或者c++)來編寫的特殊方法;
本地方法時不安全的,使用本地方法的應用程序不再能免受內存毀壞錯誤的影響。因為本地語言是平臺相關的,使用本地方法的語言不再是可移植的。
第55條:謹慎地優化
要努力編寫好的程序而不是快的程序
每次試圖優化之前和之后,要對性能進行測量
第56條:要遵循普遍的命名規范
跟著項目走,寫代碼時重視命名規范
....未完待續