《Effective Java》筆記(上)

《Effective Java》筆記(上)

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

  • Item 1: 使用static工廠方法祟牲,而不是構(gòu)造函數(shù)創(chuàng)建對象:僅僅是創(chuàng)建對象的方法,并非Factory Pattern
  • 優(yōu)點
  • 命名、接口理解更高效,通過工廠方法的函數(shù)名琳骡,而不是參數(shù)列表來表達其語義
  • Instance control,并非每次調(diào)用都會創(chuàng)建新對象讼溺,可以使用預(yù)先創(chuàng)建好的對象楣号,或者做對象緩存;便于實現(xiàn)單例怒坯;或不可實例化的類炫狱;對于immutable的對象來說,使得用==判等符合語義剔猿,且更高效视译;
  • 工廠方法能夠返回任何返回類型的子類對象,甚至是私有實現(xiàn)归敬;使得開發(fā)模塊之間通過接口耦合酷含,降低耦合度鄙早;而接口的實現(xiàn)也將更加靈活;接口不能有static方法椅亚,通常做法是為其再創(chuàng)建一個工廠方法類限番,如Collection與Collections;
  • Read More: Service Provider Framework
  • 缺點
  • 僅有static工廠方法呀舔,沒有public/protected構(gòu)造函數(shù)的類將無法被繼承扳缕;見仁見智,這一方面也迫使開發(fā)者傾向于組合而非繼承别威;
  • Javadoc中不能和其他static方法區(qū)分開,沒有構(gòu)造函數(shù)的集中顯示優(yōu)點驴剔;但可以通過公約的命名規(guī)則來改善省古;
  • 小結(jié)
    static工廠方法和public構(gòu)造函數(shù)均有其優(yōu)缺點,在編碼過程中丧失,可以先考慮一下工廠方法是否合適豺妓,再進行選擇。
  • Item 2: 使用當構(gòu)造函數(shù)的參數(shù)較多布讹,尤其是其中還有部分是可選參數(shù)時琳拭,使用Builder模式
  • 以往的方法
  • Telescoping constructor:針對可選參數(shù),從0個到最多個描验,依次編寫一個構(gòu)造函數(shù)白嘁,它們按照參數(shù)數(shù)量由少到多逐層調(diào)用,最終調(diào)用到完整參數(shù)的構(gòu)造函數(shù)膘流;代碼冗余絮缅,有時還得傳遞無意義參數(shù),而且容易導(dǎo)致使用過程中出隱蔽的bug呼股;
  • JavaBeans Pattern:靈活耕魄,但是缺乏安全性,有狀態(tài)不一致問題彭谁,線程安全問題吸奴;
  • Builder Pattern
  • 代碼靈活簡潔;具備安全性缠局;
  • immutable
  • 參數(shù)檢查:最好放在要build的對象的構(gòu)造函數(shù)中则奥,而非builder的構(gòu)建過程中
  • 支持多個field以varargs的方式設(shè)置(每個函數(shù)只能有一個varargs)
  • 一個builder可以build多個對象
  • Builder結(jié)合泛型,實現(xiàn)Abstract Factory Pattern
  • 傳統(tǒng)的抽象工廠模式甩鳄,是用Class類實現(xiàn)的逞度,然而其有缺點:newInstance調(diào)用總是去調(diào)用無參數(shù)構(gòu)造函數(shù),不能保證存在妙啃;newInstance方法會拋出所有無參數(shù)構(gòu)造函數(shù)中的異常档泽,而且不會被編譯期的異常檢查機制覆蓋俊戳;可能會導(dǎo)致運行時異常,而非編譯期錯誤馆匿;
  • 小結(jié)
    Builder模式在簡單地類(參數(shù)較少抑胎,例如4個以下)中,優(yōu)勢并不明顯渐北,但是需要予以考慮阿逃,尤其是當參數(shù)可能會變多時,有可選參數(shù)時更是如此赃蛛。
  • Item 3: 單例模式恃锉!
    不管以哪種形式實現(xiàn)單例模式,它們的核心原理都是將構(gòu)造函數(shù)私有化呕臂,并且通過靜態(tài)方法獲取一個唯一的實例破托,在這個獲取的過程中你必須保證線程安全、反序列化導(dǎo)致重新生成實例對象等問題歧蒋,該模式簡單土砂,但使用率較高。
  • double-check-locking
```java
    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可能會失效谜洽,因為指令重排可能導(dǎo)致同步解除后萝映,對象初始化不完全就被其他線程獲取阐虚;使用volatile關(guān)鍵字修飾對象序臂,或者使用static SingletonHolder來避免該問題(后者JLS推薦);

  • class的static代碼:一個類只有在被使用時才會初始化实束,而類初始化過程是非并行的贸宏,這些都由JLS能保證
  • 用enum實現(xiàn)單例
  • 還存在反射安全性問題:利用反射,可以訪問私有方法磕洪,可通過加一個控制變量吭练,該變量在getInstance函數(shù)中設(shè)置,如果不是從getInstance調(diào)用構(gòu)造函數(shù)析显,則拋出異常鲫咽;
  • Item 4: 將構(gòu)造函數(shù)私有化,使得不能從類外創(chuàng)建實例谷异,同時也能禁止類被繼承
    util類可能不希望被實例化分尸,有其需求
  • Item 5: 避免創(chuàng)建不必要的對象
  • 提高性能:創(chuàng)建對象需要時間糯耍、空間肠套,“重量級”對象尤甚;immutable的對象也應(yīng)該避免重復(fù)創(chuàng)建梳侨,例如String尺上;
  • 避免auto-boxing
  • 但是因此而故意不創(chuàng)建必要的對象是錯誤的材蛛,使用object pool通常也是沒必要的
  • lazy initialize也不是特別必要圆到,除非使用場景很少且很重量級
  • Map#keySet方法,每次調(diào)用返回的是同一個Set對象卑吭,如果修改了返回的set芽淡,其他使用的代碼可能會產(chǎn)生bug
  • 需要defensive copying的時候,如果沒有創(chuàng)建一個新對象豆赏,將導(dǎo)致很隱藏的Bug
  • Item 6: 不再使用的對象一定要解除引用挣菲,避免memory leak
  • 例如,用數(shù)組實現(xiàn)一個棧掷邦,pop的時候白胀,如果僅僅是移動下標,沒有把pop出棧的數(shù)組位置引用解除抚岗,將發(fā)生內(nèi)存泄漏
  • 程序發(fā)生錯誤之后纹笼,應(yīng)該盡快把錯誤拋出,而不是以錯誤的狀態(tài)繼續(xù)運行苟跪,否則可能導(dǎo)致更大的問題
  • 通過把變量(引用)置為null不是最好的實現(xiàn)方式,只有在極端情況下才需要這樣蔓涧;好的辦法是通過作用域來使得變量的引用過期件已,所以盡量縮小變量的作用域是很好的實踐;注意元暴,在Dalvik虛擬機中篷扩,存在一個細微的bug,可能會導(dǎo)致內(nèi)存泄漏茉盏,詳見
  • 當一個類管理了一塊內(nèi)存鉴未,用于保存其他對象(數(shù)據(jù))時,例如用數(shù)組實現(xiàn)的棧鸠姨,底層通過一個數(shù)組來管理數(shù)據(jù)铜秆,但是數(shù)組的大小不等于有效數(shù)據(jù)的大小,GC器卻并不知道這件事讶迁,所以這時候连茧,需要對其管理的數(shù)據(jù)對象進行null解引用
  • 當一個類管理了一塊內(nèi)存,用于保存其他對象(數(shù)據(jù))時巍糯,程序員應(yīng)該保持高度警惕啸驯,避免出現(xiàn)內(nèi)存泄漏,一旦數(shù)據(jù)無效之后祟峦,需要立即解除引用
  • 實現(xiàn)緩存的時候也很容易導(dǎo)致內(nèi)存泄漏罚斗,放進緩存的對象一定要有換出機制,或者通過弱引用來進行引用
  • listner和callback也有可能導(dǎo)致內(nèi)存泄漏宅楞,最好使用弱引用來進行引用针姿,使得其可以被GC
  • Item 7: 不要使用finalize方法
  • finalize方法不同于C++的析構(gòu)函數(shù)袱吆,不是用來釋放資源的好地方
  • finalize方法執(zhí)行并不及時,其執(zhí)行線程優(yōu)先級很低搓幌,而當對象unreachable之后杆故,需要執(zhí)行finalize方法之后才能釋放,所以會導(dǎo)致對象生存周期變長溉愁,甚至根本不會釋放
  • finalize方法的執(zhí)行并不保證執(zhí)行成功/完成
  • 使用finalize時处铛,性能會嚴重下降
  • finalize存在的意義
  • 充當“safety net”的角色,避免對象的使用者忘記調(diào)用顯式termination方法拐揭,盡管finalize方法的執(zhí)行時間沒有保證撤蟆,但是晚釋放資源好過不釋放資源;此處輸出log警告有利于排查bug
  • 用于釋放native peer堂污,但是當native peer持有必須要釋放的資源時家肯,應(yīng)該定義顯式termination方法
  • 子類finalize方法并不會自動調(diào)用父類finalize方法(和構(gòu)造函數(shù)不同),為了避免子類不手動調(diào)用父類的finalize方法導(dǎo)致父類的資源未被釋放盟猖,當需要使用finalize時讨衣,使用finalizer guardian比較好:
  • 定義一個私有的匿名Object子類對象,重寫其finalize方法式镐,在其中進行父類要做的工作
  • 因為當父類對象被回收時反镇,finalizer guardian也會被回收,它的finalize方法就一定會被觸發(fā)

Object的方法

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

  • Item 8: 當重寫equals方法時你弦,遵循其語義
  • 能不重寫equals時就不要重寫
  • 當對象表達的不是值惊豺,而是可變的狀態(tài)時
  • 對象不需要使用判等時
  • 父類已重寫,且滿足子類語義
  • 當需要判等禽作,且繼承實現(xiàn)無法滿足語義時尸昧,需要重寫(通常是“value class”,或immutable對象)
  • 當用作map的key時
  • 重寫equals時需要遵循的語義
  • 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(一致性): 當對象未發(fā)生改變時旷偿,多次調(diào)用應(yīng)該返回同一結(jié)果
  • x.equals(null)必須返回false
  • 實現(xiàn)建議
  • 先用==檢查是否引用同一對象彻磁,提高性能
  • 用instanceof再檢查是否同一類型
  • 再強制轉(zhuǎn)換為正確的類型
  • 再對各個域進行equals檢查,遵循同樣的規(guī)則
  • 確認其語義正確狸捅,編寫測例
  • 重寫equals時衷蜓,同時也重寫hashCode
  • !重寫equals方法尘喝,傳入的參數(shù)是Object
  • Item 9: 重寫equals時也重寫hashCode函數(shù)
  • 避免在基于hash的集合中使用時出錯
  • 語義
  • 一致性
  • 當兩個對象equals返回true時磁浇,hashCode方法的返回值也要相同
  • hashCode的計算方式
  • 要求:equals的兩個對象hashCode一樣,但是不equals的對象hashCode不一樣
  • 取一個素數(shù)朽褪,例如17置吓,result = 17
  • 對每一個關(guān)心的field(在equals中參與判斷的field)无虚,記為f,將其轉(zhuǎn)換為一個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: 先計算每個元素的hashCode,再按照int處理
  • 對每個field計算的c戴质,result = 31 * result + c
  • 返回result
  • 編寫測例
  • 計算hashCode時度宦,不重要的field(未參與equals判斷)不要參與計算
  • Item 10: 重寫toString()方法
  • 增加可讀性,簡潔告匠、可讀戈抄、具有信息量
  • Item 11: 慎重重寫clone方法
  • Cloneable接口是一個mixin interface,用于表明一個對象可以被clone
  • Contract
  • x.clone() != x
  • x.clone().getClass() == x.getClass():要求太弱后专,當一個非final類重寫clone方法的時候划鸽,創(chuàng)建的對象一定要通過super.clone()來獲得,所有父類都遵循同樣的原則戚哎,如此最終通過Object.clone()創(chuàng)建對象裸诽,能保證創(chuàng)建的是正確的類實例。而這一點很難保證型凳。
  • x.clone().equals(x)
  • 不調(diào)用構(gòu)造函數(shù):要求太強丈冬,一般都會在clone函數(shù)里面調(diào)用
  • 對于成員變量都是primitive type的類,直接調(diào)用super.clone()啰脚,然后cast為自己的類型即可(重寫時允許返回被重寫類返回類型的子類,便于使用方实夹,不必每次cast)
  • 成員變量包含對象(包括primitive type數(shù)組)橄浓,可以通過遞歸調(diào)用成員的clone方法并賦值來實現(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í)行钟沛,而此時被clone的對象狀態(tài)還未完成clone/construct畔规,會導(dǎo)致corruption。因此上一條中提及的“設(shè)置相同的值”所調(diào)用的方法恨统,要是final或者private叁扫。
  • 重載類的clone方法可以省略異常表的定義三妈,如果重寫時把可見性改為public,則應(yīng)該省略莫绣,便于使用畴蒲;如果設(shè)計為應(yīng)該被繼承,則應(yīng)該重寫得和Object的一樣对室,且不應(yīng)該實現(xiàn)Cloneable接口模燥;多線程問題也需要考慮;
  • 要實現(xiàn)clone方法的類软驰,都應(yīng)該實現(xiàn)Cloneable接口涧窒,同時把clone方法可見性設(shè)為public,返回類型為自己锭亏,應(yīng)該調(diào)用super.clone()來創(chuàng)建對象纠吴,然后手動設(shè)置每個域的值
  • clone方法太過復(fù)雜,如果不實現(xiàn)Cloneable接口慧瘤,也可以通過別的方式實現(xiàn)copy功能戴已,或者不提供copy功能,immutable提供copy功能是無意義的
  • 提供拷貝構(gòu)造函數(shù)锅减,或者拷貝工廠方法糖儡,而且此種方法更加推薦,但也有其不足
  • 設(shè)計用來被繼承的類時怔匣,如果不實現(xiàn)一個正確高效的clone重寫握联,那么其子類也將無法實現(xiàn)正確高效的clone功能
  • Item 12: 當對象自然有序時,實現(xiàn)Comparable接口
  • 實現(xiàn)Comparable接口可以利用其有序性特點每瞒,提高集合使用/搜索/排序的性能
  • Contact
  • sgn(x.compareTo(y)) == - sgn(y.compareTo(x))金闽,當類型不對時,應(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保存
  • 當要為一個實現(xiàn)了Comparable接口的類增加成員變量時贷掖,不要通過繼承來實現(xiàn)嫡秕,而是使用組合,并提供原有對象的訪問方法苹威,以保持對Contract的遵循
  • 實現(xiàn)細節(jié)
  • 優(yōu)先比較重要的域
  • 謹慎使用返回差值的方式淘菩,有可能會溢出

Classes and Interfaces

  • Item 13: 最小化類、成員的可見性
  • 封裝(隱藏):公開的接口需要暴露,而接口的實現(xiàn)則需要隱藏潮改,使得接口與實現(xiàn)解耦狭郑,降低模塊耦合度,增加可測試性汇在、穩(wěn)定性翰萨、可維護性、可優(yōu)化性糕殉、可修改性
  • 如果一個類只對一個類可見亩鬼,則應(yīng)該將其定義為私有的內(nèi)部類,而沒必要public的類都應(yīng)該定義為package private
  • 為了便于測試阿蝶,可以適當放松可見性雳锋,但也只應(yīng)該改為package private,不能更高
  • 成員不能是非private的羡洁,尤其是可變的對象玷过。一旦外部可訪問,將失去對其內(nèi)容的控制能力筑煮,而且會有多線程問題
  • 暴露的常量也不能是可變的對象辛蚊,否則public static final也將失去其意義,final成員無法改變其指向真仲,但其指向的對象卻是可變的(immutable的對象除外)袋马,長度非0的數(shù)組同樣也是有問題的,可以考慮每次訪問時創(chuàng)建拷貝秸应,或者使用Collections.unmodifiableList(Arrays.asList(arr))
  • Item 14: public class中虑凛,使用accessor method而非public field
  • 后者外部可以直接訪問,失去了安全性
  • package private或者private則可以不必這樣
  • 把immutable的field置為public勉強可以接受软啼,mutable的成員一定不能置為public
  • Item 15: 最小化可變性
  • 不提供可以改變本對象狀態(tài)的方法
  • 保證類不可被繼承
  • 使用final field
  • 使用private field
  • 在構(gòu)造函數(shù)桑谍、accessor中,對mutable field使用defensive copy
  • 實現(xiàn)建議
  • 操作函數(shù)焰宣,例如BigInteger的add方法霉囚,不是static的捕仔,但也不能改變本對象的狀態(tài)匕积,則使用functional的方式,返回一個新的對象榜跌,其狀態(tài)是本對象修改之后的狀態(tài)
  • 如此實現(xiàn)的immutable對象生來就是線程安全的闪唆,無需同步操作,但應(yīng)該鼓勵共用實例钓葫,避免創(chuàng)建過多重復(fù)的對象
  • 正確實現(xiàn)的immutable對象也不需要clone, copy方法悄蕾;可以適當引入Object cache;
  • 劣勢
  • 每一個值都需要一個對象,調(diào)用改變狀態(tài)的方法而創(chuàng)建一個新的對象帆调,尤其是它是重量級的奠骄,開銷會變大;連續(xù)調(diào)用這樣的方法番刊,影響更大含鳞;
  • 為常用的多次操作組合提供一個方法
  • 其他
  • 保證class無法被繼承,除了聲明為final外芹务,還可以將默認構(gòu)造函數(shù)聲明為private或package private蝉绷,然后提供public static工廠方法
  • 使用public static工廠方法,具體實現(xiàn)類可以有多個枣抱,還能進行object cache
  • 當實現(xiàn)Serializable接口是熔吗,一定要實現(xiàn)readObject/readResolve方法,或者使用ObjectOutputStream.writeUnshared/ObjectInputStream.readUnshared
  • 小結(jié)
  • 除非有很好的理由讓一個Class mutable佳晶,否則應(yīng)該使其immutable
  • 如果非要mutable桅狠,也應(yīng)盡可能限制其可變性
  • Item 16: Favor composition (and forwarding) over inheritance
  • 跨包繼承、繼承不是被設(shè)計為應(yīng)該被繼承的實現(xiàn)類宵晚,是一件很危險的事情垂攘,繼承接口、繼承抽象類淤刃,當然是沒問題的
  • 如果子類的功能依賴于父類的實現(xiàn)細節(jié)晒他,那么一旦父類發(fā)生變化,子類將有可能出現(xiàn)Bug逸贾,即便代碼都沒有修改陨仅;而設(shè)計為應(yīng)被繼承的類,在修改后铝侵,是應(yīng)該有文檔說明的灼伤,子類開發(fā)者既可以得知,也可以知道如何修改
  • 例子:統(tǒng)計HashSet添加元素的次數(shù)
  • 用繼承方式咪鲜,重寫add狐赡,addAll,在其中計數(shù)疟丙,這就不對颖侄,因為HashSet內(nèi)部的addAll是通過調(diào)用add實現(xiàn)的
  • 但是通過不重寫addAll也只不對的,以后有可能HashSet的實現(xiàn)就變了
  • 在重寫中重新實現(xiàn)一遍父類的邏輯也是行不通的享郊,因為這可能會導(dǎo)致性能問題览祖、bug等,而且有些功能不訪問私有成員也是無法實現(xiàn)的
  • 還有一個原因就是父類的實現(xiàn)中炊琉,可能會增加方法展蒂,改變其行為,而這一點,在子類中是無法控制的
  • 而通過組合的方式锰悼,將不會有這些問題柳骄,把另一個類的對象聲明為私有成員,外部將無法訪問它箕般,自己也能在轉(zhuǎn)發(fā)(forwarding)過程中執(zhí)行攔截操作夹界,也不必依賴其實現(xiàn)細節(jié),這種組合隘世、轉(zhuǎn)發(fā)的實現(xiàn)被稱為wrapper可柿,或者Decorator pattern,或者delegation(嚴格來說不是代理丙者,代理一般wrapper對象都需要把自己傳入到被wrap的對象方法中复斥?)
  • 缺點
  • 不適用于callback frameworks?
  • 繼承應(yīng)該在is-a的場景中使用
  • 繼承除了會繼承父類的API功能械媒,也會繼承父類的設(shè)計缺陷目锭,而組合則可以隱藏成員類的設(shè)計缺陷
  • Item 17: Design and document for inheritance or else prohibit it
  • 一個類必須在文檔中說明,每個可重寫的方法纷捞,在該類的實現(xiàn)中的哪些地方會被調(diào)用(the class must document its self-use of overridable methods)痢虹。調(diào)用時機、順序主儡、結(jié)果產(chǎn)生的影響奖唯,包括多線程、初始化等情況糜值。
  • 被繼承類應(yīng)該通過謹慎選擇protected的方法或成員丰捷,來提供一些hook,用于改變其內(nèi)部的行為寂汇,例如java.util.AbstractList::removeRange病往。
  • The only way to test a class designed for inheritance is to write subclasses. 用于判斷是否需要增加或者減少protected成員/方法,通常寫3個子類就差不多了骄瓣。
  • 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畔勤。
  • 如果實現(xiàn)了Serializable/Cloneable接口,neither clone nor readObject may invoke an overridable method, directly or indirectly. 重寫方法會在deserialized/fix the clone’s state之前執(zhí)行臼膏。
  • 如果實現(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工廠方法)渗磅。
  • 如果確實想要允許繼承,就應(yīng)該為每個被自己使用的可重寫方法都寫好文檔
  • Item 18: Prefer interfaces to abstract classes
  • Java類只允許單繼承,接口可以多繼承始鱼,使用接口定義類型仔掸,使得class hierarchy更加靈活
  • 定義mixin(optional functionality to be "mixed in")時使用interface是很方便的,需要增加此功能的類只需要implement該接口即可医清,而如果使用抽象類起暮,則無法增加一個extends語句
  • 接口允許構(gòu)建沒有hierarchy的類型系統(tǒng)
  • 使用接口定義類型,可以使得item 16中提到的wrapper模式更加安全会烙、強大负懦,
  • skeletal implementation:該類為abstract,把必須由client實現(xiàn)的方法設(shè)為abstract柏腻,可以有默認實現(xiàn)的則提供默認實現(xiàn)
  • simulated multiple inheritance:通過實現(xiàn)定義的接口纸厉,同時在內(nèi)部實現(xiàn)一個匿名的skeletal implementation,將對對該接口的調(diào)用轉(zhuǎn)發(fā)到匿名類中五嫂,起到“多繼承”的效果
  • simple implementation:提供一個非抽象的接口實現(xiàn)類颗品,提供一個最簡單、能work的實現(xiàn)沃缘,也允許被繼承
  • 使用接口定義類型的缺點:不便于演進躯枢,一旦接口發(fā)布,如果想要增加功能(增加方法)槐臀,則client將無法編譯锄蹂;而使用abstract class,則沒有此問題水慨,只需要提供默認實現(xiàn)即可
  • 小結(jié)
  • 通過接口定義類型败匹,可以允許多實現(xiàn)(多繼承)
  • 但是演進需求大于靈活性、功能性時讥巡,抽象類更合適
  • 提供接口時掀亩,提供一個skeletal implementation,同時審慎考慮接口設(shè)計
  • Item 19: 僅僅用interface去定義一個類型欢顷,該接口應(yīng)該有實現(xiàn)類槽棍,使用者通過接口引用,去調(diào)用接口的方法
  • 避免用接口去定義常量抬驴,應(yīng)該用noninstantiable utility class去定義常量
  • 相關(guān)常量的命名炼七,通過公共前綴來實現(xiàn)分組
  • Item 20: Prefer class hierarchies to tagged classes
  • tagged class: 在內(nèi)部定義一個tag變量,由其控制功能的轉(zhuǎn)換
  • tag classes are verbose, error-prone, and inefficient
  • 而class hierarchy布持,不同功能由不同子類實現(xiàn)豌拙,公共部分抽象為一個基類,也能反映出各個子類之間的關(guān)系
  • Item 21: Use function objects to represent strategies
  • 只提供一個功能函數(shù)的類實例题暖,沒有成員變量按傅,只需一個對象(單例)捉超,為其功能定義一個接口,則可以實現(xiàn)策略模式唯绍,把具體策略傳入相應(yīng)函數(shù)中拼岳,使用策略
  • 具體的策略實例通常使用匿名類定義,調(diào)用使用該策略的方法時才予以創(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的生命周期獨立于外部類存在惜纸,則必須定義為static member class,否則可能造成內(nèi)存泄漏
  • private static member class用處一:表示(封裝)外部類的一些成員绝骚,例如Map的Entry內(nèi)部類耐版。
  • non-static member class
  • 將持有外部類實例的強引用,可以直接引用外部類的成員和方法
  • 用處一:定義一個Adapter压汪,使得外部內(nèi)的實例椭更,可以作為和外部類語義不同的實例來查看(訪問),例如Collection的Iterator蛾魄。
  • 如果nested class不需要引用外部類的成員和方法虑瀑,則一定要將其定義為static,避免空間/時間開銷滴须,避免內(nèi)存泄漏
  • anonymous class
  • 當在非static代碼塊內(nèi)定義時舌狗,會持有外部類的引用,否則不會持有
  • 限制
  • 只能在被聲明的地方進行實例化
  • 無法進行instanceof測試
  • 不能用匿名類實現(xiàn)多個接口
  • 不能用匿名類繼承一個類的同時實現(xiàn)接口
  • 匿名類中新添加的方法無法在匿名類外部訪問
  • 不能有static成員
  • 應(yīng)該盡量保持簡短
  • 用處一:創(chuàng)建function object
  • 用處二:創(chuàng)建process object扔水,例如:Runnable, Thread, TimberTask
  • 用處三:用于public static工廠方法痛侍,例如Collections類里面的一些工廠方法,很多是返回一個匿名的內(nèi)部實現(xiàn)
  • local class
  • 比較少用
  • 是否static取決于其定義的上下文
  • 可以在作用域內(nèi)重復(fù)使用
  • 不能有static成員
  • 也應(yīng)盡量保持簡短
  • 小結(jié)
  • 四種nested class
  • 如果nested class在整個外部類內(nèi)都需要可見魔市,或者定義代碼太長主届,應(yīng)使用member class
  • 能static就一定要static,即便需要對外部類進行引用待德,對于生命周期獨立于外部類的君丁,也應(yīng)該通過WeakReference進行引用,避免內(nèi)存泄漏将宪;至于生命周期和外部類一致的绘闷,則不必這樣

Generics

  • Item 23: Don’t use raw types in new code
  • Java泛型,例如List<E>较坛,真正使用的時候都是List<String>等印蔗,把E替換為實際的類型
  • Java泛型從1.5引入,為了保持兼容性丑勤,實現(xiàn)的是偽泛型华嘹,類型參數(shù)信息在編譯完成之后都會被擦除,其在運行時的類型都是raw type法竞,類型參數(shù)保存的都是Object類型耙厚,List<E>的raw type就是List
  • 編譯器在編譯期通過類型參數(shù)强挫,為讀操作自動進行了類型強制轉(zhuǎn)換,同時在寫操作時自動進行了類型檢查
  • 如果使用raw type颜曾,那編譯器就不會在寫操作時進行類型檢查了,寫入錯誤的類型也不會報編譯錯誤秉剑,那么在后續(xù)讀操作進行強制類型轉(zhuǎn)換時泛豪,將會導(dǎo)致轉(zhuǎn)換失敗,拋出異常
  • 一旦錯誤發(fā)生侦鹏,應(yīng)該讓它盡早被知道(拋出/捕獲)诡曙,編譯期顯然優(yōu)于運行期
  • ListList<Object>的區(qū)別
  • 前者不具備類型安全性,后者具備略水,例如以下代碼
  ```java
    // 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);
    }
  ```
  不會報編譯錯誤价卤,但會給一個編譯警告:`Test.java:10: warning: unchecked call to add(E) in raw type List list.add(o);`,而運行時則會發(fā)生錯誤渊涝。
+  但如果使用`List<Object>`慎璧,即`unsageAdd`參數(shù)改為`List<Object> list, Object o`,則會報編譯錯誤:`Test.java:5: unsafeAdd(List<Object>,Object) cannot be applied to (List<String>,Integer) unsafeAdd(strings, new Integer(42));`  
+  因為`List<String>`是`List`的子類跨释,但卻不是`List<Object>`的子類胸私。  
+  并不是說這個場景應(yīng)該使用`List<Object>`,這個場景應(yīng)該使用`List<String>`鳖谈,這里只是為了說明`List`和`List<Object>`是有區(qū)別的岁疼。
  • List v.s. List<?>(unbounded wildcard types),當不確定類型參數(shù)缆娃,或者說類型參數(shù)不重要時捷绒,也不應(yīng)該使用raw type斑粱,而應(yīng)該使用List<?>
  • 任何參數(shù)化的List均是List<?>的子類讯壶,可以作為參數(shù)傳入接受List<?>的函數(shù),例如以下代碼均是合法的:
  ```java
    void func(List<?> list) {
      ...
    }

    func(new List<Object>());
    func(new List<Integer>());
    func(new List<String>());
  ```
+  持有`List<?>`的引用后趁窃,并不能向其中加入任何元素崇渗,讀取出來的元素也是`Object`類型它碎,而不會被自動強轉(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強轉(zhuǎn)為List
```java
  // Legitimate use of raw type - instanceof operator
  if (o instanceof Set) { // Raw type
    Set<?> m = (Set<?>) o; // Wildcard type
    ...
  }
```
  • 相關(guān)術(shù)語匯總
    java_generic_terms.png
  • Item 24: Eliminate unchecked warnings
  • 當出現(xiàn)類型不安全的強制轉(zhuǎn)換時(一般都是涉及泛型挖息,raw type),編譯器會給出警告兽肤,首先要做的是盡量消除不安全的轉(zhuǎn)換套腹,消除警告
  • 實在無法消除/確定不會導(dǎo)致運行時的ClassCastException绪抛,可以通過@SuppressWarnings("unchecked")消除警告,但不要直接忽略該警告
  • 使用@SuppressWarnings("unchecked")時电禀,應(yīng)該在注視內(nèi)證明確實不存在運行時的ClassCastException幢码;同時應(yīng)該盡量減小其作用的范圍,通常是應(yīng)該為一個賦值語句添加注解
  • Item 25: Prefer lists to arrays
  • arrays are covariant(協(xié)變): 如果SubSuper的子類尖飞,那么Sub[]也是Super[]的子類
  • generics are invariant(不變): 任意兩個不同的類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在運行時能知道且強制要求元素的類型
  • generics are implemented by erasure(non-reifiable): 僅僅在編譯時知道元素的類型
  • 數(shù)組和泛型同時使用時會受到很大限制
  • 以下語句均不能通過編譯:new List<E>[], new List<String>[], new E[];但是聲明是可以的政基,例如List<String>[] stringLists
  • non-reifiable type: 例如E, List<E>, List<String>贞铣,這些類型在運行時的信息比編譯時的信息更少
  • 只有unbounded wildcard type才是reifiable的,如:List<?>, Map<?, ?>
  • 常規(guī)來說沮明,不能返回泛型元素的數(shù)組辕坝,因為會報編譯錯誤:generic array creation errors
  • 當泛型和varargs一起使用時,也會導(dǎo)致編譯警告
  • 有時為了類型安全荐健,不得不做些妥協(xié)酱畅,犧牲性能和簡潔,使用List而不是數(shù)組
  • 把數(shù)組強轉(zhuǎn)為non-reifiable類型是非常危險的江场,僅應(yīng)在非常確定類型安全的情況下使用
  • Item 26: Favor generic types
  • 當需要一個類成員的數(shù)據(jù)類型具備一般性時圣贸,應(yīng)該用泛型,這也正是泛型的設(shè)計場景之一扛稽,不應(yīng)該用Object類
  • 但使用泛型有時也不得不進行cast吁峻,例如當泛型遇上數(shù)組
  • 總的來說把suppress數(shù)組類型強轉(zhuǎn)的unchecked warning比suppress一個標量類型強轉(zhuǎn)的unchecked warning風險更大,但有時出于代碼簡潔性考慮在张,也不得不做出妥協(xié)
  • 有時看似與item 25矛盾用含,實屬無奈,Java原生沒有List帮匾,ArrayList不得不基于數(shù)組實現(xiàn)啄骇,HashMap也是基于數(shù)組實現(xiàn)的
  • 泛型比使用者進行cast更加安全,而且由于Java泛型的擦除實現(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) {...}
  • 泛型方法要比方法使用者進行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)用均無法通過編譯,因為盡管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類型的研侣,當然應(yīng)該是E的子類(這里的extends包括E類型本身);而容納輸出是要把E賦值給傳入?yún)?shù)的炮捧,當然應(yīng)該是E的父類(同樣包括E本身)
  • 返回值類型不要使用bounded wildcards庶诡,否則使用者也需要使用,這將會給使用者造成麻煩
  • 代碼對于bounded wildcards的使用在使用者那邊應(yīng)該是透明的咆课,即他們不會感知到bounded wildcards的存在末誓,如果他們也需要考慮bounded wildcards的問題,則說明對bounded wildcards的使用有問題了
  • 有時候編譯器的類型推導(dǎo)在遇到bounded wildcards會無法完成傀蚌,這時就需要顯示指定類型信息基显,例如:
  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ù)是有限個的善炫,例如List<T>Map<K, V>库继,但有時可能需要一個容器箩艺,能放入任意類型的對象,但需要具備類型安全性宪萄,例如數(shù)據(jù)庫的一行艺谆,它的每一列都可能是任意類型的數(shù)據(jù)
  • 由于Class類從1.5就被泛型化了,所以使得這種需求可以實現(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ù),用來表述編譯時和運行時的類型信息
  • Favorites的實現(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進去侠碧,而且key可以是任意類型參數(shù)的Class對象
  • 另外抹估,Map的value類型是Object,一旦put到Map中去弄兜,其編譯期類型信息就丟失了药蜻,將通過get方法的動態(tài)類型轉(zhuǎn)換(cast)來重新獲得其類型信息
  • cast方法將檢查類型信息,如果是該類型(或其子類)替饿,轉(zhuǎn)換將成功语泽,并返回引用,否則將拋出ClassCastException
  • 這一heterogeneous container實現(xiàn)有兩個不足
  • 通過為put方法傳入Class的raw type视卢,使用者可以很輕易地破壞類型安全性湿弦,解決方案也很簡單,在put時也進行一下cast:
```java
  // 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的使用者則不會因為惡意put使用者產(chǎn)生異常颊埃。這種做法也被`java.util.Collections`包中的一些方法使用蔬充,例如命名為checkedSet, checkedList, checkedMap的類。
+  這個容器內(nèi)不能放入non-reifiable的類型班利,例如`List<String>`饥漫,因為`List<String>.class`是有語法錯誤的,`List<String>`, `List<Integer>`都只有同一個class對象:`List.class`罗标;另外`String[].class`是合法的庸队。
  • Favorites使用的類型參數(shù)是unbounded的,可以put任意類型闯割,也可以使用bounded type token彻消,使用bounded時可能需要把Class<?>轉(zhuǎn)換為Class<? extends Annotation>,直接用class.cast將會導(dǎo)致unchecked warning宙拉,可以通過class.asSubclass來進行轉(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));
  }

摘錄來源:https://notes.piasy.com/Android-Java/EffectiveJava.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谢澈,隨后出現(xiàn)的幾起案子煌贴,更是在濱河造成了極大的恐慌,老刑警劉巖锥忿,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件牛郑,死亡現(xiàn)場離奇詭異,居然都是意外死亡敬鬓,警方通過查閱死者的電腦和手機淹朋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钉答,“玉大人础芍,你說我怎么就攤上這事∠3眨” “怎么了者甲?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長砌创。 經(jīng)常有香客問我虏缸,道長,這世上最難降的妖魔是什么嫩实? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任刽辙,我火速辦了婚禮,結(jié)果婚禮上甲献,老公的妹妹穿的比我還像新娘宰缤。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布慨灭。 她就那樣靜靜地躺著朦乏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪氧骤。 梳的紋絲不亂的頭發(fā)上呻疹,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天,我揣著相機與錄音筹陵,去河邊找鬼刽锤。 笑死,一個胖子當著我的面吹牛朦佩,可吹牛的內(nèi)容都是我干的并思。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼语稠,長吁一口氣:“原來是場噩夢啊……” “哼宋彼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起颅筋,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤宙暇,失蹤者是張志新(化名)和其女友劉穎输枯,沒想到半個月后议泵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡桃熄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年先口,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞳收。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡碉京,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出螟深,到底是詐尸還是另有隱情谐宙,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布界弧,位于F島的核電站凡蜻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏垢箕。R本人自食惡果不足惜划栓,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望条获。 院中可真熱鬧忠荞,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至碧绞,卻和暖如春称诗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背头遭。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工寓免, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人计维。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓袜香,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鲫惶。 傳聞我的和親對象是個殘疾皇子蜈首,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 對象的創(chuàng)建與銷毀 Item 1: 使用static工廠方法,而不是構(gòu)造函數(shù)創(chuàng)建對象:僅僅是創(chuàng)建對象的方法欠母,并非Fa...
    孫小磊閱讀 1,990評論 0 3
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理欢策,服務(wù)發(fā)現(xiàn),斷路器赏淌,智...
    卡卡羅2017閱讀 134,693評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法踩寇,類相關(guān)的語法,內(nèi)部類的語法六水,繼承相關(guān)的語法俺孙,異常的語法,線程的語...
    子非魚_t_閱讀 31,659評論 18 399
  • 《Effective Java》筆記(下) Enums and Annotations Item 30: Use ...
    OCNYang閱讀 2,037評論 1 5
  • 積極的人像太陽场靴,照到哪里哪里亮;消極的人像月亮港准,初一十五都一樣旨剥。 當你面對一個成功者,你會發(fā)現(xiàn)對方一般都很陽光叉趣、面...
    自由飛翔的風閱讀 302評論 8 4