EffectiveJava-1-創建和銷毀對象

2021-01-07 今陽說

一. 使用靜態工廠方法代替構造器

1. 一個類對外提供獲取自身實例對象的方法:

- 提供公有構造器;

- 公有的靜態工廠方法(一個返回當前類實例的靜態方法,包括當不限於我們平時所寫的單例);

2. 靜態工廠方法的優勢 :

a. 可以隨意組合要初始化的屬性,通過不同的命名,不僅可以避免構造器的限制(一個類只能有一個帶有指定籤名的構造器),而且可以做到顧名思義;

b. 可以不用在每次調用時都創建一個新的對象,可以使用預先構建好的對象,或將構建好的對象緩存起來,進行重複利用,適用於經常請求創建相同對象,並且創建對象的代價很高,如常見的單例模式寫法就是對這一點的應用;實例受控的類:能為重複的調用返回相同的類,有助於類控制某個時刻哪些實例應該存在,功能如下:

1.確保它是一個Singleton或者是不可實例化的;

2.使得不可變的類不會存在兩個相等的實例,即當切僅當a==b時,才有a.equals(b)=true,如果保證了這一點, 就可以使用==代替equals,這樣可以提高性能,枚舉類型保證了這一點 ;

(==為hash地址相同,即同一個對象;equals為實例內容相同)

c. 可以返回原返回值類型的任何子類型的對象,有更好的靈活性,而且可以隱藏實現類(子類),使API更簡潔,適用於基於接口的框架;

(如可以用工廠方法的不同命名標識不同的子類對象,也可以同一個工廠方法,通過參數(如Type)進行判斷,返回不同類型的子類對象)

d. 創建參數化類型實例(即泛型)時,使代碼更簡潔,如:

3. 靜態工廠方法的缺點

a. 類如果不含公有的(public)或受保護的(protected)構造器,就不能被子類化,不方便擴展和形成關係體系;

(不過某些情況也可以用組合代替繼承,同樣可以實現代碼的復用)

(組合:即該類的實例做為新類的一個屬性)

b. 與其他靜態方法沒有任何區別,不方便使用者查找使用;

(可以通過添加注釋以及命名規範,彌補這個問題)

4. 實例演示:構造器與靜態工廠方法

5. 之前有自學過iOS開發,靜態工廠獲取實例的方式,在Object-c中深有體現,只是他們叫做類工廠方法,就像下面代碼這樣:

二. 遇到多個構造器參數時要考慮使用構建器(建造者模式)

如果有大量的可選參數需要任意組合,我們來想想看有哪些實現方案呢?

1. 使用構造器:不能任意的組合;

2. 使用公有靜態工廠方法:需要提前寫出足夠多組合的工廠方法,而且參數過多時通過命名區分也將很不方便;

3. 使用JavaBeans:即無參構造+setter方法,但是這使得構造過程分到了多個調用中,而且不能有效的保證一致性,還有就是阻止了把類做成不可變的可能,需要額外保證它的線程安全性;

4. 使用Builder模式:

優點:既保證了安全性,又有很好的可讀性,而且方便對參數添加約束條件,也可以自動填充某些參數而不提供給調用者去修改(即對客戶端隱藏一些屬性);

缺點:代碼量增多,可能增加內存開銷;

適用於參數比較多的情況,以上面所用對Person類為例;

通過建造者創建實例:

三. 用私有構造器或者枚舉類型強化Singleton屬性

- 實例受控;

- 單例模式的餓漢式,懶漢式,都是需要將構造器私有化;

- 單元素的枚舉類型已經成為實現 Singleton 的最佳方法;

四. 通過私有構造器強化不可實例化的能力

有些類只有靜態方法和靜態屬性,如一些工具類,比如自定義日誌工具類LogUtil,我們不希望它可以創建實例,然而一個類在缺少構造器時,系統會默認提供一個公有無參的構造方法,這時我們可以顯示的提供一個私有的構造方法,以保證該類不可實例化;

五. 避免創建不必要的對象

例1:將字符串轉為boolean類型的實現方法,使用Boolean.valueOf("true")要優於new Boolean("true"),其源碼內部實現如下:

例2:還是以上面的Person類為例,現在新增加一個判斷是否生育高峰期出生的方法;

注意這裡是說避免創建不必要的對象,而不是儘量少的創建對象;

六. 消除過期的對象引用

一般而言,只要類是自己管理內存,就應當注意內存洩漏問題;

例如:棧實現類ArrayStack,當棧先增長後收縮,棧內還維護著過期引用,這種情況稱為無意識的對象保持,那麼就需要在pop()方法中將已經出棧的元素設為空;

內存洩漏另一個常見來源是緩存,有下面幾種解決方案:

1. 使用WeakHashMap代表緩存,緩存中的項過期後就會自動被刪除;

2. 使用後臺線程定時的清除沒用的緩存,或者添加新的緩存時進行清除,使用LinkedHashMap.removeEldestEntry()就可以實現後一種方案;

3. 對於更加複雜的緩存,必須直接使用 java.lang.ref;

4. 監聽器和其他回調:要取消註冊或使用weak reference,如只將他們保存為WeakHashMap中的鍵;

七. 避免使用終結方法

finalizer通常是不可預測的,也是很危險的,一般是不必要的,除非作為安全網,或者為了終止非關鍵的本地資源,若使用了終結方法,就要記住調用super.finalize,如果需要把終結方法與公有的非final類關聯起來,請考慮使用終結方法守衛者,以確保即使子類的終結方法未能調用super.finalize, 該終結方法也會被執行;

我是今陽,如果想要進階和了解更多的乾貨,歡迎關注公眾號」今陽說「接收我的最新文章

相關焦點

  • Effective java-Lambda使用
    為了能根據時代發展,java 8中引入了lambda表達式。促進了java的函數編程,大大提升了開發效率。lambda表達式的出現,改變了Java開發者的編程習慣,但lambda應該如何更好的使用呢? effective java 中給出了說明。
  • 如何正確地創建和銷毀軟體應用系統中JDBC資料庫連接對象實例
    軟體項目實訓及課程設計指導——如何正確地創建和銷毀軟體應用系統中JDBC資料庫連接對象實例1、Java語言中提供有訪問資料庫系統的JDBC編程接口Java 資料庫連接技術(JDBC,Java DataBase Connectivity)其實是將Java語言與標準的
  • 在程序中如何正確地創建和銷毀軟體應用系統中文件IO流對象實例
    軟體項目實訓及課程設計指導——如何正確地創建和銷毀軟體應用系統中文件IO流對象實例1、Java文件輸入輸出(讀寫)相關的技術基礎知識(1)流(Stream)它是通過緩衝機制將數據從生產者(如鍵盤、磁碟文件、內存或其他設備
  • 對象的創建過程
    對象創建的步驟對象的創建是在主線程的main()方法中,所以在主線程的虛擬機棧中就會創建main()的棧幀,main()就是當前方法。我們回顧下棧和棧幀。虛擬機棧的生命周期和線程的生命周期一致,它隨著線程的創建而創建,隨著線程的銷毀而銷毀,所以它是線程私有的內存區域。虛擬機棧是由棧幀組成的,棧幀中包含了局部變量表、操作數棧、動態連結、方法返回地址、附加信息。棧幀是隨著方法的調用而創建的。
  • java創建對象的過程詳解(從內存角度分析)
    java對象的創建操作其實我在《JVM系列之類的加載機制》一文曾經提到過,包含兩個過程:類的初始化和實例化。為此為了理解的深入,我們還需要再來看一下類的生命周期。一張圖表示:從上面我們可以看到,對象的創建其實包含了初始化和使用兩個階段。有了這個印象之後,我們就能開始今天的文章了。
  • 用Java創建對象的5種不同方式
    作為Java開發人員,我們通常每天創建許多對象,但是我們始終使用依賴管理系統(例如Spring)來創建這些對象。但是,還有更多創建對象的方法,我們將在本文中進行研究。用Java創建對象的共有五種方法,下面將通過示例說明這些方法,然後介紹創建對象的行的字節碼。
  • 如何正確地創建和銷毀軟體應用系統中網絡通訊中的Socket對象實例
    軟體項目實訓及課程設計指導——如何正確地創建和銷毀軟體應用系統中網絡通訊中的Socket對象實例1、基於TCP/IP協議的Socket通信相關的基礎知識(1)TCP/IP(Transmission Control Protocol傳輸控制協議/Internet
  • Java之父都需要的《Effective Java中文版(第3版)》到底有多牛
    這是一本分享經驗並指引你少走彎路的經典著作,針對如何編寫高效、設計優良的程序提出了最實用、最權威的指導方針,通過90條經驗法則,探索新的設計模式和語言習慣用法,幫你更加有效地使用Java程式語言及其基本類庫。「我很希望我10年前就能擁有這本書。有人可能認為我不需要任何Java方面的書籍,但是我需要這本書。」
  • Java面向對象之枚舉——enum
    解決方案:定義一個類Weekday來表示周1到周日,在類中分別使用7個常量來表示周1到周日,代碼如下:和接口一樣,枚舉是一個特殊的類,其實質上就是數量固定的多個常量對象的集合。枚舉主要用來表示固定的事務類型。枚舉語法定義格式:常量之間使用英文狀態下的「,」分隔,最後一個常量之後必須要用「;」結尾。
  • 阿里內部學習指南《Effective Java中文 第3版》程式設計師進階必備
    經典Jolt獲獎作品《Effective Java》的第3版這本書,對上一版內容進行了徹底的更新,介紹了如何充分利用從泛型到枚舉、從註解到自動裝箱的各種特性,幫助讀者更加有效地使用Java程式語言及其基本類庫:java.lang. java.util和java.io,以及子包,如java.util
  • Effective java 中文版 PDF
    這本《Effective java》是必不可少的一本,豆瓣評分高達9.8分,文末有獲取方式。《Effective Java》第三版一共包含了 90 條極具實用價值的經驗規則,每條規則都值得 Java 程式設計師在實戰中去參照。
  • 一些經典Java面試題&答案解析 || 附《Effective Java》中文版
    4、請列舉java創建對象的幾種方式答案:(1) 用new語句創建對象,這是最常見的創建對象的方法。(2) 運用反射手段,調用java.lang.Class或者java.lang.reflect.Constructor類的newInstance()實例方法。(3) 調用對象的clone()方法。(4) 運用反序列化手段,調用java.io.ObjectInputStream對象的 readObject()方法。
  • Java之線程池的簡單介紹
    在之前小編介紹線程的時候,如果我們使用線程的時候,就可以去創建一個線程,那樣實現起來就非常簡單,但是如果並發地線程數量很多,並且每一個線程都是執行一個時間很短的任務就結束了,這樣頻繁創建線程就會大大降低系統的效率,頻繁創建和銷毀線程需要時間。通過使用線程池,可以使線程復用,就是執行完一個任務,不銷毀,繼續執行其他任務。
  • EffectiveJava-3-類和接口
    ,不要為getter方法編寫對應的setter方法,以保證類的不可變,getter方法可以返回保護性拷貝對象;只有當確認有必要實現令人滿意的性能時,才應該為不可變類提供公有的可變配套類,如String的可變配套類StringBuilder和StringBuffer;構造器/靜態工廠方法應該創建完全初始化的對象,並建立起所有的約束關係,不要再提供額外的公有初始化方法,或重新初始化方法
  • Java面向對象之final、abstract抽象、和變量生命周期
    其特點是:抽象類不能創建實例,也就是不能使用new創建一個抽象類對象,即使創建出抽象類對象,調用了抽象方法,也無法實現功能,因為抽象方法沒有方法體。抽象類中可以不存在抽象方法,這樣做雖然沒有太大的意義,但是可以防止外界創建其對象,所以我們會發現有些工具類沒有抽象方法,但卻是使用abstract來修飾類的。普通類有的成員(方法、欄位、構造器),抽象類本質上也是一個類,故其都有。抽象類不能創建對象,但抽象類中是可以包含普通方法的。
  • 「JAVA」屬性、路徑分隔符有何不同?file對象創建,文件過濾器
    為解決這個問題,Java 在java.io.File類中提供了兩類常量,分別來表示路徑分隔符和屬性分隔符,官方源碼如下所示:文件02創建File對象Java的I/O操作和通信的相關類和接口位於java.io包中,包中有提供大量的
  • 五種方法創建java對象,你知道幾種呢?
    作為Java開發人員,我們通常每天都會創建許多對象,但是我們總是使用新的或依賴管理系統(例如Spring)來創建這些對象。但是,有更多方法可以創建本文中將要研究的對象。用Java創建對象的共有5種核心方式,下面將以其示例加以說明,然後介紹創建對象的行的字節碼。
  • 如何合理地創建對象實例以降低程序類之間關係的耦合度
    軟體項目實訓及課程設計指導——如何合理地創建對象實例以降低程序類之間關係的耦合度1、對象創建是面向對象OOP編程技術中不可缺少的一件事情創建類的對象實例是在所有的面向對象什麼時候應該創建類的對象實例?如何保證所創建出的類的對象實例能夠適時地被銷毀?其實上面的核心問題也就是軟體應用系統的開發人員如何能夠更高效地創建、並且松藕合、達到程序模塊的可擴展性?繼續採用如下的對象實例的創建形式嗎?
  • 每日一課 | 從Java中的文件讀取對象
    反序列化的過程與序列化非常相似,您需要使用ObjectInputStream讀取文件的內容並將其轉換回Java對象。  fin = new FileInputStream("c:\\temp\\address.ser");ObjectInputStream ois = new ObjectInputStream(fin);address = (Address) ois.readObject();1.
  • 如何應用GOF設計模式中的構建者模式創建複合對象實例
    軟體項目實訓及課程設計指導——如何應用GOF設計模式中的構建者模式創建複合對象實例1、GOF設計模式中的構建者模式構建者設計模式能夠將一個複雜對象(它一般為組合類)的構建過程與它的表示部件相互分離,使得同樣的構建過程可以創建出不同的表示部件——也就是希望所獲得的目標組合對象