EffectiveJava-1-創建和銷毀對象

2021-01-11 今陽說

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

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, 該終結方法也會被執行;

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

相關焦點

  • EffectiveJava-3-類和接口
    ,不要為getter方法編寫對應的setter方法,以保證類的不可變,getter方法可以返回保護性拷貝對象;只有當確認有必要實現令人滿意的性能時,才應該為不可變類提供公有的可變配套類,如String的可變配套類StringBuilder和StringBuffer;構造器/靜態工廠方法應該創建完全初始化的對象,並建立起所有的約束關係,不要再提供額外的公有初始化方法,或重新初始化方法
  • Java面向對象之final、abstract抽象、和變量生命周期
    其特點是:抽象類不能創建實例,也就是不能使用new創建一個抽象類對象,即使創建出抽象類對象,調用了抽象方法,也無法實現功能,因為抽象方法沒有方法體。抽象類中可以不存在抽象方法,這樣做雖然沒有太大的意義,但是可以防止外界創建其對象,所以我們會發現有些工具類沒有抽象方法,但卻是使用abstract來修飾類的。普通類有的成員(方法、欄位、構造器),抽象類本質上也是一個類,故其都有。抽象類不能創建對象,但抽象類中是可以包含普通方法的。
  • 「JAVA」萬字長篇詳述字節碼對象與反射機制完成動態編程
    ,便會啟動JVM,將字節碼文件加載到JVM中,然後開始運行;當運行java命令時,該命令將會啟動一個JVM進程,在這個JVM進程中,會保存有該JVM創建的所有線程、變量、對象,這些線程、變量、對象會共享該JVM的內存區域。
  • Java經典面試題Spring是什麼 Spring框架入門詳解
    下面請看java代碼我們通過ClassPathXmlApplicationContext類傳入applicationContext.xml配置文件的相對路徑,創建出spring的容器對象ApplicationContext,在通過容器對象中的方法獲取到Spring容器為我們創建的user對象,其實Spring兩個容器,除了
  • JAVA面向對象的多態是個什麼東西?
    面向對象的三大特徵:封裝、繼承、多態。在前面已經說了過了封裝和繼承。今天就來聊聊剩下的多態。多態,從字面內容來看,就是多種形態,多種狀態。在java的面向對象中可以從以下兩個方面來講。於是我們同樣創建的動物的對象,同樣調用這個動物對象叫的方法,如果這個動物對象是貓,則會發出「喵喵」聲,而如果這個動物對象是狗,發出的聲音則成了「汪汪」。這就是不同之類之間體現出來的多態。在代碼上體現就是方法名稱,參數,返回值完全與父類相同,而不同的子類在方法體內的代碼邏輯不同。
  • JAVA專業術語面試100問
    1、面向對象的特點有哪些?抽象、繼承、封裝、多態。2、接口和抽象類有什麼聯繫和區別?3、重載和重寫有什麼區別?4、java有哪些基本數據類型?21、java中垃圾收集的方法有哪些?22、如何判斷一個對象是否存活?(或者GC對象的判定方法)?23、Java GC是在什麼時候,對什麼東西,做了什麼事情?24、什麼是類加載器雙親委派模型機制?
  • Java常見內存溢出異常分析
    Java 堆 OutOfMemoryError   Java 堆是用來存儲對象實例的, 因此如果我們不斷地創建對象, 並且保證 GC Root 和創建的對象之間有可達路徑以免對象被垃圾回收, 那麼當創建的對象過多時, 會導致 heap 內存不足, 進而引發 OutOfMemoryError 異常。
  • Java程式設計師必備基礎:Java代碼是怎麼運行的?
    運行時創建對象 方法調用,執行引擎解釋為機器碼 CPU執行指令 多線程切換上下文 編譯 我們都知道,java代碼是運行在Java虛擬機上的。
  • Java編程中基礎反射詳細解析
    類加載指的是將類的class文件讀入內存中,並為之創建一個 java.lang.Class對象,也就是說程序使用任何類的時候,都會為其創建一個class對象。類加載的時機創建類的實例的時候訪問類的靜態變量的時候調用類的靜態方法的時候使用反射方式來強制創建某個類或接口對應的java.lang.Class對象初始化某個類的子類的時候
  • 為什麼Java字符串是不可變對象?(3)
    為什麼Java字符串是不可變對象?(3) 本文主要來介紹一下Java中的不可變對象,以及Java中String類的不可變性,那麼為什麼Java的String類是不可變對象?讓我們一起來分析一下。
  • Java反射機制深入詳解
    Class類的構造方法是private,由JVM創建。反射是java語言的一個特性,它允程序在運行時(注意不是編譯的時候)來進行自我檢查並且對內部的成員進行操作。例如它允許一個java的類獲取他所有的成員變量和方法並且顯示出來。Java 的這一能力在實際應用中也許用得不是很多,但是在其它的程序設計語言中根本就不存在這一特性。
  • Java面向對象之接口——interface
    如: Walkable.java以I開頭表示接口,如:IWalkable.java接口在編譯成功之後,和類一樣,也是.class的字節碼文件。接口中的成員:1.接口中沒有構造器,因為接口不能使用new關鍵字創建對象(不能 new );2.接口中不能定義普通方法,接口中的方法都是默認為公共的抽象方法,而且都是 public abstract 修飾的;所以接口中的方法的public符 和 abstract
  • Java反射初探 ——「當類也學會照鏡子」
    等基本類型,雖然本質上不是對象,但行為卻也和對象密切相關(基本包裝類型和自動裝箱) 所以有一個可能完全打破我們常規思維的論斷是: 類也是對象「類」對象和「類」類型好吧,其實說「 類也是對象」並不太好,而應該說,java中
  • Java基礎教程:java反射機制教程
    因為這個類是我們自己定義的,所以在使用的時候我們知道User有兩個欄位name和age,還有無參和有參構造方法,另外的test方法我們也可以直接調用(因為其是public)。這時候java語言在設計的時候為我們提供了一個機制,就是反射機制,他能夠很方便的去解決我們的問題。
  • 黑馬程式設計師:Random類與ArrayList類的構造方法和創建(附習題)
    本文內容:Random類ArrayList類本文學後目標:能夠使用Random類生成隨機數能夠使用數組存儲自定義類型並遍歷能夠使用ArrayList集合構造方法創建ArrayList集合對象一、 Random類1.此類的實例用於生成偽隨機數,比如說是,下面的代碼可以使我們能夠得到一個隨機的數字
  • Java之File類的構造方法
    小編先來介紹一下構造方法1:File(String pathname)通過給定路徑名字符串轉換為抽象路徑名來創建一個新File實例參數:String pathname:字符串的路徑名稱路徑可以是以文件結尾
  • java第五章 類和對象(核心)
    修飾,並提供set和get方法以及便於顯示數據的show方法,測試類中創建對象並使用,最終控制臺輸出 林青霞,30示例代碼:/*學生類*/class Student {//成員變量private String name;private int age;//get/set方法
  • 反射——Java高級開發必須懂得
    包.類名");我們完全可以通過類的類類型創建該類的對象實例Foo foo=(Foo) c1.newInstance();二、JAVA動態加載類Class.forName("類的全稱"),不僅表示了對象的類類型
  • 嘿,你對象在這兒——Java 面向對象編程:類和對象
    首先,創建一個類,然後寫一個主方法,最後列印輸出,ojbk完事兒。等到我們學面向對象的時候不得不去了解一下什麼是類,什麼是對象,他倆是啥關係。關於面向對象,網上那個寫得很好的例子已經被我抄到C語言與Java的區別那篇了,感興趣的小夥伴可以點左上角的菜單去查看。那麼類和對象到底是什麼呢?
  • 深入分析java中的多態(從jvm角度分析)
    對於java中多態概念的理解一直是面試常問的問題,所以今天花了一些時間好好地整理了一下,力求從java虛擬機的角度來分析和理解多態。一、認識多態1、方法調用在Java中,方法調用有兩類,動態方法調用與靜態方法調用。