在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Android/ Effective Java一書筆記
Launch mode 和 Intent flags專題
Canvas & Drawables
UTAustinX_UT.9.01x: Effective Thinking Through Mathematics
《JavaScript 語言精粹》
Memory leak專題
React基礎(chǔ)
《Test Driven Development: By Example》一書
Developer tools
安卓開發(fā)技能樹
<a rel="nofollow" href="https://mp.weixin.qq.com/s?__biz=MzA3NDM
Best Practices for Interaction and Engagement
各個(gè)安卓版本引入的主要新特性
Building Apps with Connectivity &amp; the Cloud
List.toArray()再強(qiáng)轉(zhuǎn)是一定會失敗的
深入Android frameworks
Google dev 100 days系列視頻
Building Apps with Contacts &amp; Sign-In
關(guān)系型數(shù)據(jù)庫設(shè)計(jì)范式
《App研發(fā)錄》一書
REST API設(shè)計(jì)
Google IO 2015摘要
自定義View/ViewGroup以及高性能實(shí)現(xiàn)自定義UI
安卓系統(tǒng)點(diǎn)擊事件處理
《50 Android Hacks》一書
Building Apps with Content Sharing
Flux基礎(chǔ)
<a rel="nofollow" href="http://developer.android.com/training/in
依賴注入(以Dagger 2為例)
Java同步機(jī)制
Java對象內(nèi)存的使用情況
JSR133(Java memory model)
Google官方Material Design手冊(<a rel="nofollow" href="http://develop
Futurice公司安卓團(tuán)隊(duì)的建議
安卓性能優(yōu)化
  • 1.
Best Practices for Performance
<a rel="nofollow" href="http://www.vogella.com/tutorials/Android
<a rel="nofollow" href="http://blog.danlew.net/2014/11/19/styles
Handling Runtime Changes
<a rel="nofollow" href="http://www.vogella.com/tutorials/Android
Building Apps with Graphics &amp; Animation
<a rel="nofollow" href="http://tools.android.com/tech-docs/new-b
Android項(xiàng)目架構(gòu)
MVP(Model-View-Presenter)模式
<a rel="nofollow" href="http://www.infoq.com/cn/es6-in-depth/"">
《Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)》一書
Rx在Android中的最佳實(shí)踐
函數(shù)調(diào)用時(shí),傳遞參數(shù)應(yīng)該是不可變的(Immutable)
ProGuard
面向?qū)ο罅笤瓌t(SOLID+)
深入理解Java虛擬機(jī)
深入Java深淺拷貝、immutable、unmodifiable
Best Practices for User Input
UI上的一些高效方式/最佳實(shí)踐
<a rel="nofollow" href="https://blog.stylingandroid.com/ripples-
Best Practices for User Interface
安卓測試驅(qū)動開發(fā)/安卓測試驗(yàn)證
暗時(shí)間:學(xué)會正確思考
技術(shù)筆記
Aspect Oriented Programming(AOP)
Best Practices for Background Jobs
安卓系統(tǒng)動效專題
Feed系統(tǒng)的設(shè)計(jì)
Data binding(MVVM,Model-View-ViewModel)
Effective Java一書筆記
<a rel="nofollow" href="http://developer.android.com/training/in
Rx (Reactive eXtention)
MultiDex專題
一些很棒的點(diǎn)子
WebRTC

Effective Java一書筆記

對象的創(chuàng)建與銷毀

  • Item 1: 使用static工廠方法,而不是構(gòu)造函數(shù)創(chuàng)建對象:僅僅是創(chuàng)建對象的方法,并非Factory Pattern
    • 優(yōu)點(diǎn)
      • 命名、接口理解更高效,通過工廠方法的函數(shù)名,而不是參數(shù)列表來表達(dá)其語義
      • Instance control,并非每次調(diào)用都會創(chuàng)建新對象,可以使用預(yù)先創(chuàng)建好的對象,或者做對象緩存;便于實(shí)現(xiàn)單例;或不可實(shí)例化的類;對于immutable的對象來說,使得用==判等符合語義,且更高效;
      • 工廠方法能夠返回任何返回類型的子類對象,甚至是私有實(shí)現(xiàn);使得開發(fā)模塊之間通過接口耦合,降低耦合度;而接口的實(shí)現(xiàn)也將更加靈活;接口不能有static方法,通常做法是為其再創(chuàng)建一個(gè)工廠方法類,如Collection與Collections;
      • Read More: Service Provider Framework
    • 缺點(diǎn)
      • 僅有static工廠方法,沒有public/protected構(gòu)造函數(shù)的類將無法被繼承;見仁見智,這一方面也迫使開發(fā)者傾向于組合而非繼承;
      • Javadoc中不能和其他static方法區(qū)分開,沒有構(gòu)造函數(shù)的集中顯示優(yōu)點(diǎn);但可以通過公約的命名規(guī)則來改善;
    • 小結(jié)
      static工廠方法和public構(gòu)造函數(shù)均有其優(yōu)缺點(diǎn),在編碼過程中,可以先考慮一下工廠方法是否合適,再進(jìn)行選擇。
  • Item 2: 使用當(dāng)構(gòu)造函數(shù)的參數(shù)較多,尤其是其中還有部分是可選參數(shù)時(shí),使用Builder模式
    • 以往的方法
      • Telescoping constructor:針對可選參數(shù),從0個(gè)到最多個(gè),依次編寫一個(gè)構(gòu)造函數(shù),它們按照參數(shù)數(shù)量由少到多逐層調(diào)用,最終調(diào)用到完整參數(shù)的構(gòu)造函數(shù);代碼冗余,有時(shí)還得傳遞無意義參數(shù),而且容易導(dǎo)致使用過程中出隱蔽的bug;
      • JavaBeans Pattern:靈活,但是缺乏安全性,有狀態(tài)不一致問題,線程安全問題;
    • Builder Pattern
      • 代碼靈活簡潔;具備安全性;
      • immutable
      • 參數(shù)檢查:最好放在要build的對象的構(gòu)造函數(shù)中,而非builder的構(gòu)建過程中
      • 支持多個(gè)field以varargs的方式設(shè)置(每個(gè)函數(shù)只能有一個(gè)varargs)
      • 一個(gè)builder可以build多個(gè)對象
      • Builder結(jié)合泛型,實(shí)現(xiàn)Abstract Factory Pattern
      • 傳統(tǒng)的抽象工廠模式,是用Class類實(shí)現(xiàn)的,然而其有缺點(diǎn):newInstance調(diào)用總是去調(diào)用無參數(shù)構(gòu)造函數(shù),不能保證存在;newInstance方法會拋出所有無參數(shù)構(gòu)造函數(shù)中的異常,而且不會被編譯期的異常檢查機(jī)制覆蓋;可能會導(dǎo)致運(yùn)行時(shí)異常,而非編譯期錯(cuò)誤;
    • 小結(jié)
      Builder模式在簡單地類(參數(shù)較少,例如4個(gè)以下)中,優(yōu)勢并不明顯,但是需要予以考慮,尤其是當(dāng)參數(shù)可能會變多時(shí),有可選參數(shù)時(shí)更是如此。
  • Item 3: 單例模式!
    不管以哪種形式實(shí)現(xiàn)單例模式,它們的核心原理都是將構(gòu)造函數(shù)私有化,并且通過靜態(tài)方法獲取一個(gè)唯一的實(shí)例,在這個(gè)獲取的過程中你必須保證線程安全、反序列化導(dǎo)致重新生成實(shí)例對象等問題,該模式簡單,但使用率較高。

    • double-check-locking

          private static volatile RestAdapter sRestAdapter = null;
          public static RestAdapter provideRestAdapter() {
              if (sRestAdapter == null) {
                  synchronized (RestProvider.class) {
                      if (sRestAdapter == null) {
                          sRestAdapter = new RestAdapter();
                      }
                  }
              }
      
              return sRestAdapter;
          }

      DCL可能會失效,因?yàn)橹噶钪嘏趴赡軐?dǎo)致同步解除后,對象初始化不完全就被其他線程獲取;使用volatile關(guān)鍵字修飾對象,或者使用static SingletonHolder來避免該問題(后者JLS推薦);

    • class的static代碼:一個(gè)類只有在被使用時(shí)才會初始化,而類初始化過程是非并行的,這些都由JLS能保證
    • 用enum實(shí)現(xiàn)單例
    • 還存在反射安全性問題:利用反射,可以訪問私有方法,可通過加一個(gè)控制變量,該變量在getInstance函數(shù)中設(shè)置,如果不是從getInstance調(diào)用構(gòu)造函數(shù),則拋出異常;
  • Item 4: 將構(gòu)造函數(shù)私有化,使得不能從類外創(chuàng)建實(shí)例,同時(shí)也能禁止類被繼承
    util類可能不希望被實(shí)例化,有其需求
  • Item 5: 避免創(chuàng)建不必要的對象
    • 提高性能:創(chuàng)建對象需要時(shí)間、空間,“重量級”對象尤甚;immutable的對象也應(yīng)該避免重復(fù)創(chuàng)建,例如String;
    • 避免auto-boxing
    • 但是因此而故意不創(chuàng)建必要的對象是錯(cuò)誤的,使用object pool通常也是沒必要的
    • lazy initialize也不是特別必要,除非使用場景很少且很重量級
    • Map#keySet方法,每次調(diào)用返回的是同一個(gè)Set對象,如果修改了返回的set,其他使用的代碼可能會產(chǎn)生bug
    • 需要defensive copying的時(shí)候,如果沒有創(chuàng)建一個(gè)新對象,將導(dǎo)致很隱藏的Bug
  • Item 6: 不再使用的對象一定要解除引用,避免memory leak
    • 例如,用數(shù)組實(shí)現(xiàn)一個(gè)棧,pop的時(shí)候,如果僅僅是移動下標(biāo),沒有把pop出棧的數(shù)組位置引用解除,將發(fā)生內(nèi)存泄漏
    • 程序發(fā)生錯(cuò)誤之后,應(yīng)該盡快把錯(cuò)誤拋出,而不是以錯(cuò)誤的狀態(tài)繼續(xù)運(yùn)行,否則可能導(dǎo)致更大的問題
    • 通過把變量(引用)置為null不是最好的實(shí)現(xiàn)方式,只有在極端情況下才需要這樣;好的辦法是通過作用域來使得變量的引用過期,所以盡量縮小變量的作用域是很好的實(shí)踐;注意,在Dalvik虛擬機(jī)中,存在一個(gè)細(xì)微的bug,可能會導(dǎo)致內(nèi)存泄漏,詳見
    • 當(dāng)一個(gè)類管理了一塊內(nèi)存,用于保存其他對象(數(shù)據(jù))時(shí),例如用數(shù)組實(shí)現(xiàn)的棧,底層通過一個(gè)數(shù)組來管理數(shù)據(jù),但是數(shù)組的大小不等于有效數(shù)據(jù)的大小,GC器卻并不知道這件事,所以這時(shí)候,需要對其管理的數(shù)據(jù)對象進(jìn)行null解引用
    • 當(dāng)一個(gè)類管理了一塊內(nèi)存,用于保存其他對象(數(shù)據(jù))時(shí),程序員應(yīng)該保持高度警惕,避免出現(xiàn)內(nèi)存泄漏,一旦數(shù)據(jù)無效之后,需要立即解除引用
    • 實(shí)現(xiàn)緩存的時(shí)候也很容易導(dǎo)致內(nèi)存泄漏,放進(jìn)緩存的對象一定要有換出機(jī)制,或者通過弱引用來進(jìn)行引用
    • listner和callback也有可能導(dǎo)致內(nèi)存泄漏,最好使用弱引用來進(jìn)行引用,使得其可以被GC
  • Item 7: 不要使用finalize方法
    • finalize方法不同于C++的析構(gòu)函數(shù),不是用來釋放資源的好地方
    • finalize方法執(zhí)行并不及時(shí),其執(zhí)行線程優(yōu)先級很低,而當(dāng)對象unreachable之后,需要執(zhí)行finalize方法之后才能釋放,所以會導(dǎo)致對象生存周期變長,甚至根本不會釋放
    • finalize方法的執(zhí)行并不保證執(zhí)行成功/完成
    • 使用finalize時(shí),性能會嚴(yán)重下降
    • finalize存在的意義
      • 充當(dāng)“safety net”的角色,避免對象的使用者忘記調(diào)用顯式termination方法,盡管finalize方法的執(zhí)行時(shí)間沒有保證,但是晚釋放資源好過不釋放資源;此處輸出log警告有利于排查bug
      • 用于釋放native peer,但是當(dāng)native peer持有必須要釋放的資源時(shí),應(yīng)該定義顯式termination方法
    • 子類finalize方法并不會自動調(diào)用父類finalize方法(和構(gòu)造函數(shù)不同),為了避免子類不手動調(diào)用父類的finalize方法導(dǎo)致父類的資源未被釋放,當(dāng)需要使用finalize時(shí),使用finalizer guardian比較好:
      • 定義一個(gè)私有的匿名Object子類對象,重寫其finalize方法,在其中進(jìn)行父類要做的工作
      • 因?yàn)楫?dāng)父類對象被回收時(shí),finalizer guardian也會被回收,它的finalize方法就一定會被觸發(fā)

Object的方法

盡管Object不是抽象類,但是其定義的非final方法設(shè)計(jì)的時(shí)候都是希望被重寫的,finalize除外。

  • Item 8: 當(dāng)重寫equals方法時(shí),遵循其語義
    • 能不重寫equals時(shí)就不要重寫
      • 當(dāng)對象表達(dá)的不是值,而是可變的狀態(tài)時(shí)
      • 對象不需要使用判等時(shí)
      • 父類已重寫,且滿足子類語義
    • 當(dāng)需要判等,且繼承實(shí)現(xiàn)無法滿足語義時(shí),需要重寫(通常是“value class”,或immutable對象)
    • 當(dāng)用作map的key時(shí)
    • 重寫equals時(shí)需要遵循的語義
      • Reflexive(自反性): x.equals(x)必須返回true(x不為null)
      • Symmetric(對稱性): x.equals(y) == y.equals(x)
      • Transitive(傳遞性): x.equals(y) && y.equals(z) ==> x.equals(z)
      • Consistent(一致性): 當(dāng)對象未發(fā)生改變時(shí),多次調(diào)用應(yīng)該返回同一結(jié)果
      • x.equals(null)必須返回false
    • 實(shí)現(xiàn)建議
      • 先用==檢查是否引用同一對象,提高性能
      • 用instanceof再檢查是否同一類型
      • 再強(qiáng)制轉(zhuǎn)換為正確的類型
      • 再對各個(gè)域進(jìn)行equals檢查,遵循同樣的規(guī)則
      • 確認(rèn)其語義正確,編寫測例
      • 重寫equals時(shí),同時(shí)也重寫hashCode
      • !重寫equals方法,傳入的參數(shù)是Object
  • Item 9: 重寫equals時(shí)也重寫hashCode函數(shù)
    • 避免在基于hash的集合中使用時(shí)出錯(cuò)
    • 語義
      • 一致性
      • 當(dāng)兩個(gè)對象equals返回true時(shí),hashCode方法的返回值也要相同
    • hashCode的計(jì)算方式
      • 要求:equals的兩個(gè)對象hashCode一樣,但是不equals的對象hashCode不一樣
      • 取一個(gè)素?cái)?shù),例如17,result = 17
      • 對每一個(gè)關(guān)心的field(在equals中參與判斷的field),記為f,將其轉(zhuǎn)換為一個(gè)int,記為c
        • boolean: f ? 1 : 0
        • byte/char/short/int: (int) f
        • long: (int) (f ^ (f >> 32))
        • float: Float.floatToIntBits(f)
        • double: Double.doubleToLongBits(f),再按照long處理
        • Object: f == null ? 0 : f.hashCode()
        • array: 先計(jì)算每個(gè)元素的hashCode,再按照int處理
      • 對每個(gè)field計(jì)算的c,result = 31 * result + c
      • 返回result
      • 編寫測例
    • 計(jì)算hashCode時(shí),不重要的field(未參與equals判斷)不要參與計(jì)算
  • Item 10: 重寫toString()方法
    • 增加可讀性,簡潔、可讀、具有信息量
  • Item 11: 慎重重寫clone方法
    • Cloneable接口是一個(gè)mixin interface,用于表明一個(gè)對象可以被clone
    • Contract
      • x.clone() != x
      • x.clone().getClass() == x.getClass():要求太弱,當(dāng)一個(gè)非final類重寫clone方法的時(shí)候,創(chuàng)建的對象一定要通過super.clone()來獲得,所有父類都遵循同樣的原則,如此最終通過Object.clone()創(chuàng)建對象,能保證創(chuàng)建的是正確的類實(shí)例。而這一點(diǎn)很難保證。
      • x.clone().equals(x)
      • 不調(diào)用構(gòu)造函數(shù):要求太強(qiáng),一般都會在clone函數(shù)里面調(diào)用
    • 對于成員變量都是primitive type的類,直接調(diào)用super.clone(),然后cast為自己的類型即可(重寫時(shí)允許返回被重寫類返回類型的子類,便于使用方,不必每次cast)
    • 成員變量包含對象(包括primitive type數(shù)組),可以通過遞歸調(diào)用成員的clone方法并賦值來實(shí)現(xiàn)
    • 然而上述方式違背了final的使用協(xié)議,final成員不允許再次賦值,然而clone方法里面必須要對其賦值,則無法使用final保證不可變性了
    • 遞歸調(diào)用成員的clone方法也會存在性能問題,對HashTable遞歸調(diào)用深拷貝也可能導(dǎo)致StackOverFlow(可以通過遍歷添加來避免)
    • 優(yōu)雅的方式是通過super.clone()創(chuàng)建對象,然后為成員變量設(shè)置相同的值,而不是簡單地遞歸調(diào)用成員的clone方法
    • 和構(gòu)造函數(shù)一樣,在clone的過程中,不能調(diào)用non final的方法,如果調(diào)用虛函數(shù),那么該函數(shù)會優(yōu)先執(zhí)行,而此時(shí)被clone的對象狀態(tài)還未完成clone/construct,會導(dǎo)致corruption。因此上一條中提及的“設(shè)置相同的值”所調(diào)用的方法,要是final或者private。
    • 重載類的clone方法可以省略異常表的定義,如果重寫時(shí)把可見性改為public,則應(yīng)該省略,便于使用;如果設(shè)計(jì)為應(yīng)該被繼承,則應(yīng)該重寫得和Object的一樣,且不應(yīng)該實(shí)現(xiàn)Cloneable接口;多線程問題也需要考慮;
    • 要實(shí)現(xiàn)clone方法的類,都應(yīng)該實(shí)現(xiàn)Cloneable接口,同時(shí)把clone方法可見性設(shè)為public,返回類型為自己,應(yīng)該調(diào)用super.clone()來創(chuàng)建對象,然后手動設(shè)置每個(gè)域的值
    • clone方法太過復(fù)雜,如果不實(shí)現(xiàn)Cloneable接口,也可以通過別的方式實(shí)現(xiàn)copy功能,或者不提供copy功能,immutable提供copy功能是無意義的
    • 提供拷貝構(gòu)造函數(shù),或者拷貝工廠方法,而且此種方法更加推薦,但也有其不足
    • 設(shè)計(jì)用來被繼承的類時(shí),如果不實(shí)現(xiàn)一個(gè)正確高效的clone重寫,那么其子類也將無法實(shí)現(xiàn)正確高效的clone功能
  • Item 12: 當(dāng)對象自然有序時(shí),實(shí)現(xiàn)Comparable接口
    • 實(shí)現(xiàn)Comparable接口可以利用其有序性特點(diǎn),提高集合使用/搜索/排序的性能
    • Contact
      • sgn(x.compareTo(y)) == - sgn(y.compareTo(x)),當(dāng)類型不對時(shí),應(yīng)該拋出ClassCastException,拋出異常的行為應(yīng)該是一致的
      • transitive: x.compareTo(y) > 0 && y.compareTo(z) > 0 ==> x.compareTo(z) > 0
      • x.compareTo(y) == 0 ==> sgn(x.compareTo(z)) == sgn(y.compareTo(z))
      • 建議,但非必須:與equals保持一致,即 x.compareTo(y) == 0 ==> x.equals(y),如果不一致,需要在文檔中明確指出
    • TreeSet, TreeMap等使用的就是有序保存,而HashSet, HashMap則是通過equals + hashCode保存
    • 當(dāng)要為一個(gè)實(shí)現(xiàn)了Comparable接口的類增加成員變量時(shí),不要通過繼承來實(shí)現(xiàn),而是使用組合,并提供原有對象的訪問方法,以保持對Contract的遵循
    • 實(shí)現(xiàn)細(xì)節(jié)
      • 優(yōu)先比較重要的域
      • 謹(jǐn)慎使用返回差值的方式,有可能會溢出

Classes and Interfaces

  • Item 13: 最小化類、成員的可見性
    • 封裝(隱藏):公開的接口需要暴露,而接口的實(shí)現(xiàn)則需要隱藏,使得接口與實(shí)現(xiàn)解耦,降低模塊耦合度,增加可測試性、穩(wěn)定性、可維護(hù)性、可優(yōu)化性、可修改性
    • 如果一個(gè)類只對一個(gè)類可見,則應(yīng)該將其定義為私有的內(nèi)部類,而沒必要public的類都應(yīng)該定義為package private
    • 為了便于測試,可以適當(dāng)放松可見性,但也只應(yīng)該改為package private,不能更高
    • 成員不能是非private的,尤其是可變的對象。一旦外部可訪問,將失去對其內(nèi)容的控制能力,而且會有多線程問題
    • 暴露的常量也不能是可變的對象,否則public static final也將失去其意義,final成員無法改變其指向,但其指向的對象卻是可變的(immutable的對象除外),長度非0的數(shù)組同樣也是有問題的,可以考慮每次訪問時(shí)創(chuàng)建拷貝,或者使用Collections.unmodifiableList(Arrays.asList(arr))
  • Item 14: public class中,使用accessor method而非public field
    • 后者外部可以直接訪問,失去了安全性
    • package private或者private則可以不必這樣
    • 把immutable的field置為public勉強(qiáng)可以接受,mutable的成員一定不能置為public
  • Item 15: 最小化可變性
    • 不提供可以改變本對象狀態(tài)的方法
    • 保證類不可被繼承
    • 使用final field
    • 使用private field
    • 在構(gòu)造函數(shù)、accessor中,對mutable field使用defensive copy
    • 實(shí)現(xiàn)建議
      • 操作函數(shù),例如BigInteger的add方法,不是static的,但也不能改變本對象的狀態(tài),則使用functional的方式,返回一個(gè)新的對象,其狀態(tài)是本對象修改之后的狀態(tài)
      • 如此實(shí)現(xiàn)的immutable對象生來就是線程安全的,無需同步操作,但應(yīng)該鼓勵(lì)共用實(shí)例,避免創(chuàng)建過多重復(fù)的對象
      • 正確實(shí)現(xiàn)的immutable對象也不需要clone, copy方法;可以適當(dāng)引入Object cache;
    • 劣勢
      • 每一個(gè)值都需要一個(gè)對象,調(diào)用改變狀態(tài)的方法而創(chuàng)建一個(gè)新的對象,尤其是它是重量級的,開銷會變大;連續(xù)調(diào)用這樣的方法,影響更大;
      • 為常用的多次操作組合提供一個(gè)方法
    • 其他
      • 保證class無法被繼承,除了聲明為final外,還可以將默認(rèn)構(gòu)造函數(shù)聲明為private或package private,然后提供public static工廠方法
      • 使用public static工廠方法,具體實(shí)現(xiàn)類可以有多個(gè),還能進(jìn)行object cache
      • 當(dāng)實(shí)現(xiàn)Serializable接口是,一定要實(shí)現(xiàn)readObject/readResolve方法,或者使用ObjectOutputStream.writeUnshared/ObjectInputStream.readUnshared
    • 小結(jié)
      • 除非有很好的理由讓一個(gè)Class mutable,否則應(yīng)該使其immutable
      • 如果非要mutable,也應(yīng)盡可能限制其可變性
  • Item 16: Favor composition (and forwarding) over inheritance
    • 跨包繼承、繼承不是被設(shè)計(jì)為應(yīng)該被繼承的實(shí)現(xiàn)類,是一件很危險(xiǎn)的事情,繼承接口、繼承抽象類,當(dāng)然是沒問題的
    • 如果子類的功能依賴于父類的實(shí)現(xiàn)細(xì)節(jié),那么一旦父類發(fā)生變化,子類將有可能出現(xiàn)Bug,即便代碼都沒有修改;而設(shè)計(jì)為應(yīng)被繼承的類,在修改后,是應(yīng)該有文檔說明的,子類開發(fā)者既可以得知,也可以知道如何修改
    • 例子:統(tǒng)計(jì)HashSet添加元素的次數(shù)
      • 用繼承方式,重寫add,addAll,在其中計(jì)數(shù),這就不對,因?yàn)镠ashSet內(nèi)部的addAll是通過調(diào)用add實(shí)現(xiàn)的
      • 但是通過不重寫addAll也只不對的,以后有可能HashSet的實(shí)現(xiàn)就變了
      • 在重寫中重新實(shí)現(xiàn)一遍父類的邏輯也是行不通的,因?yàn)檫@可能會導(dǎo)致性能問題、bug等,而且有些功能不訪問私有成員也是無法實(shí)現(xiàn)的
      • 還有一個(gè)原因就是父類的實(shí)現(xiàn)中,可能會增加方法,改變其行為,而這一點(diǎn),在子類中是無法控制的
    • 而通過組合的方式,將不會有這些問題,把另一個(gè)類的對象聲明為私有成員,外部將無法訪問它,自己也能在轉(zhuǎn)發(fā)(forwarding)過程中執(zhí)行攔截操作,也不必依賴其實(shí)現(xiàn)細(xì)節(jié),這種組合、轉(zhuǎn)發(fā)的實(shí)現(xiàn)被稱為wrapper,或者Decorator pattern,或者delegation(嚴(yán)格來說不是代理,代理一般wrapper對象都需要把自己傳入到被wrap的對象方法中?)
    • 缺點(diǎn)
      • 不適用于callback frameworks?
    • 繼承應(yīng)該在is-a的場景中使用
    • 繼承除了會繼承父類的API功能,也會繼承父類的設(shè)計(jì)缺陷,而組合則可以隱藏成員類的設(shè)計(jì)缺陷
  • Item 17: Design and document for inheritance or else prohibit it
    • 一個(gè)類必須在文檔中說明,每個(gè)可重寫的方法,在該類的實(shí)現(xiàn)中的哪些地方會被調(diào)用(the class must document its self-use of overridable methods)。調(diào)用時(shí)機(jī)、順序、結(jié)果產(chǎn)生的影響,包括多線程、初始化等情況。
    • 被繼承類應(yīng)該通過謹(jǐn)慎選擇protected的方法或成員,來提供一些hook,用于改變其內(nèi)部的行為,例如java.util.AbstractList::removeRange。
    • The only way to test a class designed for inheritance is to write subclasses. 用于判斷是否需要增加或者減少protected成員/方法,通常寫3個(gè)子類就差不多了。
    • You must test your class by writing subclasses before you release it.
    • Constructors must not invoke overridable methods. 父類的構(gòu)造函數(shù)比子類的構(gòu)造函數(shù)先執(zhí)行,而如果父類構(gòu)造函數(shù)中調(diào)用了可重寫的方法,那么就會導(dǎo)致子類的重寫方法比子類的構(gòu)造函數(shù)先執(zhí)行,會導(dǎo)致corruption。
    • 如果實(shí)現(xiàn)了Serializable/Cloneable接口,neither clone nor readObject may invoke an overridable method, directly or indirectly. 重寫方法會在deserialized/fix the clone’s state之前執(zhí)行。
    • 如果實(shí)現(xiàn)了Serializable接口,readResolve/writeReplace必須是protected,而非private
    • designing a class for inheritance places substantial limitations on the class.
    • The best solution to this problem is to prohibit subclassing in classes that are not designed and documented to be safely subclassed. 聲明為final class或者把構(gòu)造函數(shù)私有化(提供public static工廠方法)。
    • 如果確實(shí)想要允許繼承,就應(yīng)該為每個(gè)被自己使用的可重寫方法都寫好文檔
  • Item 18: Prefer interfaces to abstract classes
    • Java類只允許單繼承,接口可以多繼承,使用接口定義類型,使得class hierarchy更加靈活
    • 定義mixin(optional functionality to be "mixed in")時(shí)使用interface是很方便的,需要增加此功能的類只需要implement該接口即可,而如果使用抽象類,則無法增加一個(gè)extends語句
    • 接口允許構(gòu)建沒有hierarchy的類型系統(tǒng)
    • 使用接口定義類型,可以使得item 16中提到的wrapper模式更加安全、強(qiáng)大,
    • skeletal implementation:該類為abstract,把必須由client實(shí)現(xiàn)的方法設(shè)為abstract,可以有默認(rèn)實(shí)現(xiàn)的則提供默認(rèn)實(shí)現(xiàn)
    • simulated multiple inheritance:通過實(shí)現(xiàn)定義的接口,同時(shí)在內(nèi)部實(shí)現(xiàn)一個(gè)匿名的skeletal implementation,將對對該接口的調(diào)用轉(zhuǎn)發(fā)到匿名類中,起到“多繼承”的效果
    • simple implementation:提供一個(gè)非抽象的接口實(shí)現(xiàn)類,提供一個(gè)最簡單、能work的實(shí)現(xiàn),也允許被繼承
    • 使用接口定義類型的缺點(diǎn):不便于演進(jìn),一旦接口發(fā)布,如果想要增加功能(增加方法),則client將無法編譯;而使用abstract class,則沒有此問題,只需要提供默認(rèn)實(shí)現(xiàn)即可
    • 小結(jié)
      • 通過接口定義類型,可以允許多實(shí)現(xiàn)(多繼承)
      • 但是演進(jìn)需求大于靈活性、功能性時(shí),抽象類更合適
      • 提供接口時(shí),提供一個(gè)skeletal implementation,同時(shí)審慎考慮接口設(shè)計(jì)
  • Item 19: 僅僅用interface去定義一個(gè)類型,該接口應(yīng)該有實(shí)現(xiàn)類,使用者通過接口引用,去調(diào)用接口的方法
    • 避免用接口去定義常量,應(yīng)該用noninstantiable utility class去定義常量
    • 相關(guān)常量的命名,通過公共前綴來實(shí)現(xiàn)分組
  • Item 20: Prefer class hierarchies to tagged classes
    • tagged class: 在內(nèi)部定義一個(gè)tag變量,由其控制功能的轉(zhuǎn)換
    • tag classes are verbose, error-prone, and inefficient
    • 而class hierarchy,不同功能由不同子類實(shí)現(xiàn),公共部分抽象為一個(gè)基類,也能反映出各個(gè)子類之間的關(guān)系
  • Item 21: Use function objects to represent strategies
    • 只提供一個(gè)功能函數(shù)的類實(shí)例,沒有成員變量,只需一個(gè)對象(單例),為其功能定義一個(gè)接口,則可以實(shí)現(xiàn)策略模式,把具體策略傳入相應(yīng)函數(shù)中,使用策略
    • 具體的策略實(shí)例通常使用匿名類定義,調(diào)用使用該策略的方法時(shí)才予以創(chuàng)建/預(yù)先創(chuàng)建好之后每次將其傳入
  • Item 22: Favor static member classes over nonstatic
    • 有4種nested class:non-static member class; static member class(inner class); anonymous class; local class
    • static member class
      • 經(jīng)常作為helper class,和外部類一起使用
      • 如果nested class的生命周期獨(dú)立于外部類存在,則必須定義為static member class,否則可能造成內(nèi)存泄漏
      • private static member class用處一:表示(封裝)外部類的一些成員,例如Map的Entry內(nèi)部類。
    • non-static member class
      • 將持有外部類實(shí)例的強(qiáng)引用,可以直接引用外部類的成員和方法
      • 用處一:定義一個(gè)Adapter,使得外部內(nèi)的實(shí)例,可以作為和外部類語義不同的實(shí)例來查看(訪問),例如Collection的Iterator。
      • 如果nested class不需要引用外部類的成員和方法,則一定要將其定義為static,避免空間/時(shí)間開銷,避免內(nèi)存泄漏
    • anonymous class
      • 當(dāng)在非static代碼塊內(nèi)定義時(shí),會持有外部類的引用,否則不會持有
      • 限制
        • 只能在被聲明的地方進(jìn)行實(shí)例化
        • 無法進(jìn)行instanceof測試
        • 不能用匿名類實(shí)現(xiàn)多個(gè)接口
        • 不能用匿名類繼承一個(gè)類的同時(shí)實(shí)現(xiàn)接口
        • 匿名類中新添加的方法無法在匿名類外部訪問
        • 不能有static成員
      • 應(yīng)該盡量保持簡短
      • 用處一:創(chuàng)建function object
      • 用處二:創(chuàng)建process object,例如:Runnable, Thread, TimberTask
      • 用處三:用于public static工廠方法,例如Collections類里面的一些工廠方法,很多是返回一個(gè)匿名的內(nèi)部實(shí)現(xiàn)
    • local class
      • 比較少用
      • 是否static取決于其定義的上下文
      • 可以在作用域內(nèi)重復(fù)使用
      • 不能有static成員
      • 也應(yīng)盡量保持簡短
    • 小結(jié)
      • 四種nested class
      • 如果nested class在整個(gè)外部類內(nèi)都需要可見,或者定義代碼太長,應(yīng)使用member class
      • 能static就一定要static,即便需要對外部類進(jìn)行引用,對于生命周期獨(dú)立于外部類的,也應(yīng)該通過WeakReference進(jìn)行引用,避免內(nèi)存泄漏;至于生命周期和外部類一致的,則不必這樣

Generics

  • Item 23: Don’t use raw types in new code

    • Java泛型,例如List<E>,真正使用的時(shí)候都是List<String>等,把E替換為實(shí)際的類型
    • Java泛型從1.5引入,為了保持兼容性,實(shí)現(xiàn)的是偽泛型,類型參數(shù)信息在編譯完成之后都會被擦除,其在運(yùn)行時(shí)的類型都是raw type,類型參數(shù)保存的都是Object類型,List<E>的raw type就是List
    • 編譯器在編譯期通過類型參數(shù),為讀操作自動進(jìn)行了類型強(qiáng)制轉(zhuǎn)換,同時(shí)在寫操作時(shí)自動進(jìn)行了類型檢查
    • 如果使用raw type,那編譯器就不會在寫操作時(shí)進(jìn)行類型檢查了,寫入錯(cuò)誤的類型也不會報(bào)編譯錯(cuò)誤,那么在后續(xù)讀操作進(jìn)行強(qiáng)制類型轉(zhuǎn)換時(shí),將會導(dǎo)致轉(zhuǎn)換失敗,拋出異常
    • 一旦錯(cuò)誤發(fā)生,應(yīng)該讓它盡早被知道(拋出/捕獲),編譯期顯然優(yōu)于運(yùn)行期
    • ListList<Object>的區(qū)別

      • 前者不具備類型安全性,后者具備,例如以下代碼

          // Uses raw type (List) - fails at runtime!
          public static void main(String[] args) {
            List<String> strings = new ArrayList<String>();
            unsafeAdd(strings, new Integer(42));
            String s = strings.get(0); // Compiler-generated cast
          }
        
          private static void unsafeAdd(List list, Object o) {
            list.add(o);
          }

        不會報(bào)編譯錯(cuò)誤,但會給一個(gè)編譯警告:Test.java:10: warning: unchecked call to add(E) in raw type List list.add(o);,而運(yùn)行時(shí)則會發(fā)生錯(cuò)誤。

      • 但如果使用List<Object>,即unsageAdd參數(shù)改為List<Object> list, Object o,則會報(bào)編譯錯(cuò)誤:Test.java:5: unsafeAdd(List<Object>,Object) cannot be applied to (List<String>,Integer) unsafeAdd(strings, new Integer(42));
      • 因?yàn)?code>List<String>是List的子類,但卻不是List<Object>的子類。
      • 并不是說這個(gè)場景應(yīng)該使用List<Object>,這個(gè)場景應(yīng)該使用List<String>,這里只是為了說明ListList<Object>是有區(qū)別的。
    • List v.s. List<?>(unbounded wildcard types),當(dāng)不確定類型參數(shù),或者說類型參數(shù)不重要時(shí),也不應(yīng)該使用raw type,而應(yīng)該使用List<?>

      • 任何參數(shù)化的List均是List<?>的子類,可以作為參數(shù)傳入接受List<?>的函數(shù),例如以下代碼均是合法的:

          void func(List<?> list) {
            ...
          }
        
          func(new List<Object>());
          func(new List<Integer>());
          func(new List<String>());
      • 持有List<?>的引用后,并不能向其中加入任何元素,讀取出來的元素也是Object類型,而不會被自動強(qiáng)轉(zhuǎn)為任何類型。
      • 如果List<?>的行為不能滿足需求,可以考慮使用模板方法,或者List<E extends XXX>(bounded wildcard types)
    • You must use raw types in class literals.
      • List.class, String[].class, and int.class are all legal, but List<String>.class and List<?>.class are not.
    • instanceof不支持泛型,以下用法是推薦的,但不應(yīng)該將o強(qiáng)轉(zhuǎn)為List
        // Legitimate use of raw type - instanceof operator
        if (o instanceof Set) { // Raw type
          Set<?> m = (Set<?>) o; // Wildcard type
          ...
        }
    • 相關(guān)術(shù)語匯總
      http://wiki.jikexueyuan.com/project/notes/images/java_generic_terms.png" alt="java_generic_terms.png" />
  • Item 24: Eliminate unchecked warnings
    • 當(dāng)出現(xiàn)類型不安全的強(qiáng)制轉(zhuǎn)換時(shí)(一般都是涉及泛型,raw type),編譯器會給出警告,首先要做的是盡量消除不安全的轉(zhuǎn)換,消除警告
    • 實(shí)在無法消除/確定不會導(dǎo)致運(yùn)行時(shí)的ClassCastException,可以通過@SuppressWarnings("unchecked")消除警告,但不要直接忽略該警告
    • 使用@SuppressWarnings("unchecked")時(shí),應(yīng)該在注視內(nèi)證明確實(shí)不存在運(yùn)行時(shí)的ClassCastException;同時(shí)應(yīng)該盡量減小其作用的范圍,通常是應(yīng)該為一個(gè)賦值語句添加注解
  • Item 25: Prefer lists to arrays

    • arrays are covariant(協(xié)變): 如果SubSuper的子類,那么Sub[]也是Super[]的子類
    • generics are invariant(不變): 任意兩個(gè)不同的類Type1Type2,List<Type1>List<Type2>之間沒有任何繼承關(guān)系
    • 考慮以下代碼

      // Fails at runtime!
      Object[] objectArray = new Long[1];
      objectArray[0] = "I don't fit in"; // Throws ArrayStoreException
      
      // Won't compile!
      List<Object> ol = new ArrayList<Long>(); // Incompatible types
      ol.add("I don't fit in");
    • arrays are reified(具體化): array在運(yùn)行時(shí)能知道且強(qiáng)制要求元素的類型
    • generics are implemented by erasure(non-reifiable): 僅僅在編譯時(shí)知道元素的類型
    • 數(shù)組和泛型同時(shí)使用時(shí)會受到很大限制
      • 以下語句均不能通過編譯:new List<E>[], new List<String>[], new E[];但是聲明是可以的,例如List<String>[] stringLists
    • non-reifiable type: 例如E, List<E>, List<String>,這些類型在運(yùn)行時(shí)的信息比編譯時(shí)的信息更少
    • 只有unbounded wildcard type才是reifiable的,如:List<?>, Map<?, ?>
    • 常規(guī)來說,不能返回泛型元素的數(shù)組,因?yàn)闀?bào)編譯錯(cuò)誤:generic array creation errors
    • 當(dāng)泛型和varargs一起使用時(shí),也會導(dǎo)致編譯警告
    • 有時(shí)為了類型安全,不得不做些妥協(xié),犧牲性能和簡潔,使用List而不是數(shù)組
    • 把數(shù)組強(qiáng)轉(zhuǎn)為non-reifiable類型是非常危險(xiǎn)的,僅應(yīng)在非常確定類型安全的情況下使用
  • Item 26: Favor generic types
    • 當(dāng)需要一個(gè)類成員的數(shù)據(jù)類型具備一般性時(shí),應(yīng)該用泛型,這也正是泛型的設(shè)計(jì)場景之一,不應(yīng)該用Object類
    • 但使用泛型有時(shí)也不得不進(jìn)行cast,例如當(dāng)泛型遇上數(shù)組
    • 總的來說把suppress數(shù)組類型強(qiáng)轉(zhuǎn)的unchecked warning比suppress一個(gè)標(biāo)量類型強(qiáng)轉(zhuǎn)的unchecked warning風(fēng)險(xiǎn)更大,但有時(shí)出于代碼簡潔性考慮,也不得不做出妥協(xié)
    • 有時(shí)看似與item 25矛盾,實(shí)屬無奈,Java原生沒有List,ArrayList不得不基于數(shù)組實(shí)現(xiàn),HashMap也是基于數(shù)組實(shí)現(xiàn)的
    • 泛型比使用者進(jìn)行cast更加安全,而且由于Java泛型的擦除實(shí)現(xiàn),也可以和未做泛型的老代碼無縫兼容
  • Item 27: Favor generic methods
    • 泛型方法的類型參數(shù)在函數(shù)修飾符(可見性/static/final等)和返回值之間,例子:
      // Generic method
      public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
          Set<E> result = new HashSet<>(s1);
          result.addAll(s2);
          return result;
      }
    • recursive type bound
      // Using a recursive type bound to express mutual comparability
      public static <T extends Comparable<T>> T max(List<T> list) {...}
    • 泛型方法要比方法使用者進(jìn)行cast更加安全
  • Item 28: Use bounded wildcards to increase API flexibility

    • 考慮以下代碼

      public class Stack<E> {
          public Stack();
          public void push(E e);
          public E pop();
          public boolean isEmpty();
      
          public void pushAll(Iterable<E> src);
          public void popAll(Collection<E> dst);
      }
      
      Stack<Number> numberStack = new Stack<Number>();
      Iterable<Integer> integers = ... ;
      numberStack.pushAll(integers);
      
      Stack<Number> numberStack = new Stack<Number>();
      Collection<Object> objects = ... ;
      numberStack.popAll(objects);

      pushAll和popAll的調(diào)用均無法通過編譯,因?yàn)楸M管IntegerNumber的子類,但Iterable<Integer>不是Iterable<Number>的子類,這是由泛型的invariant特性導(dǎo)致的,所以Iterable<Integer>不能傳入接受Iterable<Number>參數(shù)的函數(shù),popAll的使用同理

    • bounded wildcards: <? extends E>, <? super E>, PECS stands for producer-extends, consumer-super. 如果傳入的參數(shù)是要輸入給該類型數(shù)據(jù)的,則應(yīng)該使用extends,如果是要容納該類型數(shù)據(jù)的輸出,則應(yīng)該使用super
    • 這很好理解,作為輸入是要賦值給E類型的,當(dāng)然應(yīng)該是E的子類(這里的extends包括E類型本身);而容納輸出是要把E賦值給傳入?yún)?shù)的,當(dāng)然應(yīng)該是E的父類(同樣包括E本身)
    • 返回值類型不要使用bounded wildcards,否則使用者也需要使用,這將會給使用者造成麻煩
    • 代碼對于bounded wildcards的使用在使用者那邊應(yīng)該是透明的,即他們不會感知到bounded wildcards的存在,如果他們也需要考慮bounded wildcards的問題,則說明對bounded wildcards的使用有問題了
    • 有時(shí)候編譯器的類型推導(dǎo)在遇到bounded wildcards會無法完成,這時(shí)就需要顯示指定類型信息,例如:

      public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2);
      
      Set<Integer> integers = ... ;
      Set<Double> doubles = ... ;
      //Set<Number> numbers = union(integers, doubles); //compile error
      Set<Number> numbers = Union.<Number>union(integers, doubles);  //compile pass
    • Comparables are always consumers, so you should always use Comparable<? super T> in preference to Comparable<T>. The same is true of comparators, so you should always use Comparator<? super T> in preference to Comparator<T>.
    • unbounded type parameter(<E> ... List<E>) v.s. unbounded wildcard(List<?>):if a type parameter appears only once in a method declaration, replace it with a wildcard.
  • Item 29: Consider typesafe heterogeneous containers

    • 使用泛型時(shí),類型參數(shù)是有限個(gè)的,例如List<T>,Map<K, V>,但有時(shí)可能需要一個(gè)容器,能放入任意類型的對象,但需要具備類型安全性,例如數(shù)據(jù)庫的一行,它的每一列都可能是任意類型的數(shù)據(jù)
    • 由于Class類從1.5就被泛型化了,所以使得這種需求可以實(shí)現(xiàn),例如:
      // Typesafe heterogeneous container pattern - API
      public class Favorites {
          public <T> void putFavorite(Class<T> type, T instance);
          public <T> T getFavorite(Class<T> type);
      }
    • 通常這樣使用的Class對象被稱為type token,它傳入函數(shù),用來表述編譯時(shí)和運(yùn)行時(shí)的類型信息
    • Favorites的實(shí)現(xiàn)也是很簡單的:

      // Typesafe heterogeneous container pattern - implementation
      public class Favorites {
          private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>();
      
          public <T> void putFavorite(Class<T> type, T instance) {
              if (type == null)
              throw new NullPointerException("Type is null");
              favorites.put(type, instance);
          }
      
          public <T> T getFavorite(Class<T> type) {
              return type.cast(favorites.get(type));
          }
      }
    • 注意,這里的unbound wildcard并不是應(yīng)用于Map的,而是應(yīng)用于Class的類型參數(shù),因此Map可以put key進(jìn)去,而且key可以是任意類型參數(shù)的Class對象
    • 另外,Map的value類型是Object,一旦put到Map中去,其編譯期類型信息就丟失了,將通過get方法的動態(tài)類型轉(zhuǎn)換(cast)來重新獲得其類型信息
    • cast方法將檢查類型信息,如果是該類型(或其子類),轉(zhuǎn)換將成功,并返回引用,否則將拋出ClassCastException
    • 這一heterogeneous container實(shí)現(xiàn)有兩個(gè)不足
      • 通過為put方法傳入Class的raw type,使用者可以很輕易地破壞類型安全性,解決方案也很簡單,在put時(shí)也進(jìn)行一下cast:
        // Achieving runtime type safety with a dynamic cast
        public <T> void putFavorite(Class<T> type, T instance) {
            favorites.put(type, type.cast(instance));
        }

        這樣做的效果是使得想要破壞類型安全性的put使用者產(chǎn)生異常,而使用get的使用者則不會因?yàn)閻阂鈖ut使用者產(chǎn)生異常。這種做法也被java.util.Collections包中的一些方法使用,例如命名為checkedSet, checkedList, checkedMap的類。

      • 這個(gè)容器內(nèi)不能放入non-reifiable的類型,例如List<String>,因?yàn)?code>List<String>.class是有語法錯(cuò)誤的,List<String>, List<Integer>都只有同一個(gè)class對象:List.class;另外String[].class是合法的。
    • Favorites使用的類型參數(shù)是unbounded的,可以put任意類型,也可以使用bounded type token,使用bounded時(shí)可能需要把Class<?>轉(zhuǎn)換為Class<? extends Annotation>,直接用class.cast將會導(dǎo)致unchecked warning,可以通過class.asSubclass來進(jìn)行轉(zhuǎn)換,例子:
      // Use of asSubclass to safely cast to a bounded type token
      static Annotation getAnnotation(AnnotatedElement element, String annotationTypeName) {
          Class<?> annotationType = null; // Unbounded type token
          try {
              annotationType = Class.forName(annotationTypeName);
          } catch (Exception ex) {
              throw new IllegalArgumentException(ex);
          }
          return element.getAnnotation(annotationType.asSubclass(Annotation.class));
      }

Enums and Annotations

  • Item 30: Use enums instead of int constants

    • 類型安全
    • 可以為常量提供數(shù)據(jù)和方法的綁定
    • 可以遍歷
    • 實(shí)現(xiàn)建議

      • 如果是通用的,應(yīng)該定義為top level enum,否則應(yīng)定義為內(nèi)部類
      • constant-specific method implementations
        // Enum type with constant-specific method implementations
        public enum Operation {
            PLUS   { double apply(double x, double y){return x + y;} },
            MINUS  { double apply(double x, double y){return x - y;} },
            TIMES  { double apply(double x, double y){return x * y;} },
            DIVIDE { double apply(double x, double y){return x / y;} };
            abstract double apply(double x, double y);
        }
      • 結(jié)合constant-specific data

        // Enum type with constant-specific class bodies and data
        public enum Operation {
            PLUS("+") {
                double apply(double x, double y) { return x + y; }
            },
            MINUS("-") {
                double apply(double x, double y) { return x - y; }
            },
            TIMES("*") {
                double apply(double x, double y) { return x * y; }
            },
            DIVIDE("/") {
                double apply(double x, double y) { return x / y; }
            };
        
            private final String symbol;
            Operation(String symbol) { this.symbol = symbol; }
        
            @Override public String toString() { return symbol; }
            abstract double apply(double x, double y);
        }
      • If switch statements on enums are not a good choice for implementing con- stant-specific behavior on enums, what are they good for? Switches on enums are good for augmenting external enum types with constant-specific behavior.
    • A minor performance disadvantage of enums over int constants is that there is a space and time cost to load and initialize enum types.
    • 所以,在安卓設(shè)備(手機(jī)、平板)上,應(yīng)該避免使用enum,減小空間和時(shí)間的開銷
  • Item 31: Use instance fields instead of ordinals
    • 每個(gè)enum的常量都有一個(gè)ordinal()方法獲取其在該enum類型中的位置,但該方法只應(yīng)該在實(shí)現(xiàn)EnumSet, EnumMap等類型的時(shí)候被使用,其他情形都不應(yīng)該被使用
    • 如果需要為每一個(gè)常量綁定一個(gè)數(shù)據(jù),可以使用instance field實(shí)現(xiàn),如果需要綁定方法,則可以用constant-specific method implementations,參考上一個(gè)item
  • Item 32: Use EnumSet instead of bit fields
    • bit fields的方式不優(yōu)雅、容易出錯(cuò)、沒有類型安全性
    • EnumSet則沒有這些缺點(diǎn),而且對于大多數(shù)enum類型來說,其性能都和bit field相當(dāng)
    • 通用建議:聲明變量時(shí),不要用實(shí)現(xiàn)類型,應(yīng)該用接口類型,例如,應(yīng)該用List<Integer>而不是ArrayList<Integer>
    • EnumSet并非immutable的,可以通過Conllections.unmodifiableSet來封裝為immutable,但是代碼簡潔性與性能都將受到影響
  • Item 33: Use EnumMap instead of ordinal indexing

    • 同前文所述,應(yīng)該避免使用ordinal。當(dāng)需要用enum作為下標(biāo)從數(shù)組獲取數(shù)據(jù)時(shí),可以換個(gè)角度思考,以enum作為key從map里面獲取數(shù)據(jù)。
    • 數(shù)組和泛型不兼容,因此使用數(shù)組也會導(dǎo)致編譯警告;而且ordinal的值本來就不是表達(dá)index含義的,極易導(dǎo)致隱蔽錯(cuò)誤
    • EnumMap內(nèi)部使用數(shù)組實(shí)現(xiàn),因此性能和數(shù)組相當(dāng)
    • 使用數(shù)組也會導(dǎo)致程序可擴(kuò)展性下降,考慮以下兩種實(shí)現(xiàn)

      // Using ordinal() to index array of arrays - DON'T DO THIS!
      public enum Phase {
          SOLID, LIQUID, GAS;
      
          public enum Transition {
            MELT, FREEZE, BOIL, CONDENSE, SUBLIME, DEPOSIT;
      
            // Rows indexed by src-ordinal, cols by dst-ordinal
            private static final Transition[][] TRANSITIONS = {
                    { null,    MELT,     SUBLIME },
                    { FREEZE,  null,     BOIL    },
                    { DEPOSIT, CONDENSE, null    }
            };
      
            // Returns the phase transition from one phase to another
            public static Transition from(Phase src, Phase dst) {
              return TRANSITIONS[src.ordinal()][dst.ordinal()];
            }
          }
      }
      
      // Using a nested EnumMap to associate data with enum pairs
      public enum Phase {
          SOLID, LIQUID, GAS;
      
          public enum Transition {
              MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID),
              BOIL(LIQUID, GAS),   CONDENSE(GAS, LIQUID),
              SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID);
      
              final Phase src;
              final Phase dst;
      
              Transition(Phase src, Phase dst) {
                  this.src = src;
                  this.dst = dst;
              }
      
              // Initialize the phase transition map
              private static final Map<Phase, Map<Phase,Transition>> m =
                  new EnumMap<Phase, Map<Phase,Transition>>(Phase.class);
              static {
                  for (Phase p : Phase.values())
                      m.put(p,new EnumMap<Phase,Transition>(Phase.class));
                  for (Transition trans : Transition.values())
                      m.get(trans.src).put(trans.dst, trans);
              }
      
              public static Transition from(Phase src, Phase dst) {
                  return m.get(src).get(dst);
              }
          }
      }

      當(dāng)需要增加Phase時(shí),前者需要謹(jǐn)慎地修改TRANSITIONS數(shù)組的內(nèi)容(這一步驟容易出錯(cuò)),而后者則只需要增加相應(yīng)Transition即可,from函數(shù)的邏輯完全不受影響。

  • Item 34: Emulate extensible enums with interfaces
    • 當(dāng)enum遇到可擴(kuò)展性時(shí),總是一個(gè)糟糕的問題;擴(kuò)展類是基礎(chǔ)類的實(shí)例,但反過來不是,這一點(diǎn)很讓人困惑;想要枚舉所有基礎(chǔ)類和擴(kuò)展類的enum對象時(shí),并沒有一個(gè)很好地辦法;
    • 而對于可擴(kuò)展性的需求,是真實(shí)存在的,例如:operation codes (opcodes)
    • 實(shí)現(xiàn)方式是通過定義一個(gè)接口,enum類型(基礎(chǔ)與擴(kuò)展)均實(shí)現(xiàn)該接口,而在使用enum的地方,接收這個(gè)接口作為參數(shù)
    • enum類型是不可擴(kuò)展的,但是interface具備可擴(kuò)展性,如果API使用接口而非實(shí)現(xiàn)去代表operation,API就有了可擴(kuò)展性
    • 泛型高級用法:<T extends Enum<T> & Operation> ... Class<T>,T類型是enum類型,且是Operation子類
    • 這一方式的不足:enum類型對接口的實(shí)現(xiàn)是不能繼承的
  • Item 35: Prefer annotations to naming patterns
    • 在1.5之前,naming patterns很常見,在JUnit中都是這樣,例如要求測例方法一test開頭
    • naming patterns有很多問題
      • 拼寫錯(cuò)誤不能及時(shí)發(fā)現(xiàn)
      • 無法保證naming patterns只在正確的場景使用,例如可能有人以test開頭命名測例類,方法卻沒有,JUnit則不會運(yùn)行測例
      • 沒有值/類型信息,編譯器無法提前發(fā)現(xiàn)問題
    • 使用annotations可以很好的解決這些問題,但是annotations的功能也是有限的
      • @Retention(RetentionPolicy.RUNTIME)能限定其保留時(shí)期
      • @Target(ElementType.METHOD)能限定其應(yīng)用的程序元素
      • 還有其他meta-annotations,如@IntDef
    • annotations接收的參數(shù)如果是數(shù)組,為其賦值一個(gè)單獨(dú)的元素也是合法的
  • Item 36: Consistently use the Override annotation
    • @Override會使得重寫的準(zhǔn)確性得到檢查
    • 重載和重寫的區(qū)別:一個(gè)只是函數(shù)名一樣,通過參數(shù)列表決定執(zhí)行哪個(gè)版本,是編譯時(shí)多態(tài);一個(gè)是通過虛函數(shù)機(jī)制實(shí)現(xiàn),是運(yùn)行時(shí)多態(tài);
  • Item 37: Use marker interfaces to define types
    • 定義一個(gè)空的接口,表明某個(gè)類型的屬性,例如Serializable
    • 另一種方式是使用annotation,表明者其具有某種屬性
    • marker interface的優(yōu)點(diǎn)
      • 定義了一個(gè)類型,可以進(jìn)行instanceof判斷,可以聲明參數(shù)類型
      • 比annotation更簡潔
    • marker annotation的優(yōu)點(diǎn)
      • 當(dāng)一個(gè)類型(通過interface或者annotation)被聲明后,如果想要加入更多的信息,annotation更方便,即annotation對修改是開放的,因?yàn)樗膶傩钥梢杂心J(rèn)值,而interface則不行,定義了方法就必須實(shí)現(xiàn)
      • annotation可以被應(yīng)用到更多代碼的元素中,不僅僅是類型
    • 實(shí)現(xiàn)建議
      • 如果僅僅只應(yīng)用于類型,則應(yīng)該優(yōu)先考慮annotation
      • 如果希望mark的對象被限定于某個(gè)接口的實(shí)例(即為一個(gè)接口增加另外一種語義,卻不改變其API),可以考慮使用marker interface