[收藏文章]《Java編程思想》之重點筆記

1、組合和繼承之間的選擇

組合和繼承都允許在新的類中放置子對象,組合是顯式的這樣做,而繼承則是隱式的做。

  • 組合技術(shù)通常用于想在新類中使用現(xiàn)有類的功能而非它的接口這種情形。即在新類中嵌入某個對象,讓其實現(xiàn)所需要的功能,但新類的用戶看到的只是為新類所定義的接口,而非所嵌入對象的接口。為取得此效果,需要在新類中嵌入一個現(xiàn)有類的private對象。但有時,允許類的用戶直接訪問新類中的組合成分是極具意義的,即將成員對象聲明為public。如果成員對象自身都隱藏了具體實現(xiàn),那么這種做法是安全的。當用戶能夠了解到你正在組裝一組部件時,會使得端口更加易于理解。
    比如Car對象可由public的Engine對象、Wheel對象、Window對象和Door對象組合。

  • 在繼承的時候,使用某個現(xiàn)有類,并開發(fā)一個它的特殊版本。通常,這意味著你在使用一個通用類,并為了某種特殊需要而將其特殊化。稍微思考一下就會發(fā)現(xiàn),用一個“交通工具”對象來構(gòu)成一部“車子”是毫無意義的,因為“車子”并不包含“交通工具”,它僅是一種交通工具(is-a關(guān)系)。

  • “is-a”(是一個)的關(guān)系是用繼承來表達的,而“has-a”(有一個)的關(guān)系則是用組合來表達的。

到底是該用組合還是繼承,一個最清晰的判斷方法就是問一問自己是否需要從新類向基類進行向上轉(zhuǎn)型,需要的話就用繼承,不需要的話就用組合方式。

2、final關(guān)鍵字

  • final的誤解?
    當final修飾的是基本數(shù)據(jù)類型時,它指的是數(shù)值恒定不變(就是編譯期常量,如果是static final修飾,則強調(diào)只有一份)
    而對對象引用而不是基本類型運用final時,其含義會有一點令人迷惑,因為用于對象引用時,final使引用恒定不變,一旦引用被初始化指向一個對象,就無法再把它指向另一個對象。然而,對象其自身卻是可以被修改的,Java并未提供使任何對象恒定不變的途徑(但可以自己編寫類以取得使對象恒定不變的效果),這一限制同樣適用數(shù)組,它也是對象。

  • 使用final方法真的可以提高程序效率嗎?
    將一個方法設(shè)成final后,編譯器就可以把對那個方法的所有調(diào)用都置入“嵌入”調(diào)用里。只要編譯器發(fā)現(xiàn)一個final方法調(diào)用,就會(根據(jù)它自己的判斷)忽略為執(zhí)行方法調(diào)用機制而采取的常規(guī)代碼插入方法(將自變量壓入堆棧;跳至方法代碼并執(zhí)行它;跳回來;清除堆棧自變量;最后對返回值進行處理)。
    相反,它會用方法主體內(nèi)實際代碼的一個副本來替換方法調(diào)用。這樣做可避免方法調(diào)用時的系統(tǒng)開銷。當然,若方法體積太大,那么程序也會變得雍腫,可能受到到不到嵌入代碼所帶來的任何性能提升。因為任何提升都被花在方法內(nèi)部的時間抵消了。
    在最近的Java版本中,虛擬機(特別是hotspot技術(shù))能自動偵測這些情況,并頗為“明智”地決定是否嵌入一個final 方法。然而,最好還是不要完全相信編譯器能正確地作出所有判斷。通常,只有在方法的代碼量非常少,或者想明確禁止方法被覆蓋的時候,才應(yīng)考慮將一個方法設(shè)為final。
    類內(nèi)所有private 方法都自動成為final。由于我們不能訪問一個private 方法,所以它絕對不會被其他方法覆蓋(若強行這樣做,編譯器會給出錯誤提示)??蔀橐粋€private方法添加final指示符,但卻不能為那個方法提供任何額外的含義。

3、策略設(shè)計模式與適配器模式的區(qū)別

  • 策略設(shè)計模式
    創(chuàng)建一個能夠根據(jù)所傳遞的參數(shù)對象的不同而具有不同行為的方法,被稱為策略設(shè)計模式,這類方法包含所要執(zhí)行的算法中固定不變的部分,而“策略”包含變化的部分。策略就是傳遞進去的參數(shù)對象,它包含要執(zhí)行的代碼。

  • 適配器模式
    在你無法修改你想要使用的類時,可以使用適配器模式,適配器中的代碼將接受你所擁有的接口,并產(chǎn)生你所需要的接口。

4、內(nèi)部類

內(nèi)部類與組合是完全不同的概念,這一點很重要。

為什么需要內(nèi)部類?
主要是解決了多繼承的問題,繼承具體或抽象類
一般來說,內(nèi)部類繼承自某個類或?qū)崿F(xiàn)某個接口,內(nèi)部類的代碼操作創(chuàng)建它的外圍類的對象。所以可以認為內(nèi)部類提供了某種進入其外圍類的窗口。
內(nèi)部類最吸引人的原因是:每個內(nèi)部類都能獨立地繼承自一個(接口的)實現(xiàn),所以無論外圍類是否已經(jīng)繼承了某個(接口的)實現(xiàn),對于內(nèi)部類都沒有影響。
如果沒有內(nèi)部類提供的、可以繼承多個具體的或抽象的類的能力,一些設(shè)計與編程問題就很難解決。從這個角度看,內(nèi)部類使得多重繼承的解決方案變得完整。接口解決了部分問題,而內(nèi)部類有效的實現(xiàn)了“多重繼承”。也就是說,內(nèi)部類允許繼承多個非接口類型。
考慮這樣一種情形:如果必須在一個類中以某種方式實現(xiàn)兩個接口。由于接口的靈活性,你有兩種選擇:使用單一類或者使用內(nèi)部類。但如果擁有的是抽象的類或具體的類,而不是接口,那就只能使用內(nèi)部類才能實現(xiàn)多重繼承。

使用內(nèi)部類,還可以獲得其他一些特性:

  • 內(nèi)部類可以有多個實例,每個實例都有自己的狀態(tài)信息,并且與其外圍類對象的信息相互獨立。
  • 在單個外圍類中,可以讓多個內(nèi)部類以不同的方式實現(xiàn)同一個接口或繼承同一個類。
  • 創(chuàng)建內(nèi)部類對象的時刻并不依賴于外圍類對象的創(chuàng)建。
  • 內(nèi)部類并沒有令人迷惑的is-a關(guān)系,它就是一個獨立的實體。

5、序列化控制

當我們對序列化進行控制時,可能某個特定子對象不想讓Java序列化機制自動保存與恢復(fù)。如果子對象表示的是我們不希望將其序列化的敏感信息(如密碼),通常會面臨這種情況。即使對象中的這些信息是private屬性,一經(jīng)序列化處理,人們就可以通過讀取文件或者攔截網(wǎng)絡(luò)傳輸?shù)姆绞絹碓L問到它。
有兩種辦法可以防止對象的敏感部分被序列化:
實現(xiàn)Externalizable代替實現(xiàn)Serializable接口來對序列化過程進行控制,Externalizable繼承了Serializable接口,同時增添了兩個方法:writeExternal()和readExternal()。

兩者在反序列化時的區(qū)別:

  • 對Serializable對象反序列化時,由于Serializable對象完全以它存儲的二進制位為基礎(chǔ)來構(gòu)造,因此并不會調(diào)用任何構(gòu)造函數(shù),因此Serializable類無需默認構(gòu)造函數(shù),但是當Serializable類的父類沒有實現(xiàn)Serializable接口時,反序列化過程會調(diào)用父類的默認構(gòu)造函數(shù),因此該父類必需有默認構(gòu)造函數(shù),否則會拋異常。
  • 對Externalizable對象反序列化時,會先調(diào)用類的不帶參數(shù)的構(gòu)造方法,這是有別于默認反序列方式的。如果把類的不帶參數(shù)的構(gòu)造方法刪除,或者把該構(gòu)造方法的訪問權(quán)限設(shè)置為private、默認或protected級別,會拋出java.io.InvalidException: no valid constructor異常,因此Externalizable對象必須有默認構(gòu)造函數(shù),而且必需是public的。
  • Externalizable的替代方法:如果不是特別堅持實現(xiàn)Externalizable接口,那么還有另一種方法。我們可以實現(xiàn)Serializable接口,并添加writeObject()和readObject()的方法。一旦對象被序列化或者重新裝配,就會分別調(diào)用那兩個方法。也就是說,只要提供了這兩個方法,就會優(yōu)先使用它們,而不考慮默認的序列化機制。

這些方法必須含有下列準確的簽名:

private void writeObject(ObjectOutputStream stream) throws IOException

private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
  • 可以用transient關(guān)鍵字逐個字段地關(guān)閉序列化,它的意思是“不用麻煩你保存或恢復(fù)數(shù)據(jù)—我自己會處理的”。由于Externalizable對象在默認情況下不保存它們的任何字段,所以transient關(guān)鍵字只能和Serializable對象一起使用。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java中的多態(tài)性理解(注意與C++區(qū)分) Java中除了static方法和final方法(private方...
    小敏紙閱讀 1,528評論 0 19
  • JAVA序列化機制的深入研究 對象序列化的最主要的用處就是在傳遞,和保存對象(object)的時候,保證對象的完整...
    時待吾閱讀 11,167評論 0 24
  • 1.import static是Java 5增加的功能,就是將Import類中的靜態(tài)方法,可以作為本類的靜態(tài)方法來...
    XLsn0w閱讀 1,419評論 0 2
  • 對象的創(chuàng)建與銷毀 Item 1: 使用static工廠方法,而不是構(gòu)造函數(shù)創(chuàng)建對象:僅僅是創(chuàng)建對象的方法,并非Fa...
    孫小磊閱讀 2,177評論 0 3
  • 我已經(jīng)寫好了開頭 卻不知道筆怎么往下繼續(xù)走 只好停下來,回眸 你可尾隨著我等著為我分憂 我已經(jīng)寫好了開頭 卻感到這...
    靜鈴音閱讀 484評論 25 27

友情鏈接更多精彩內(nèi)容