Effective Java——第二章,創建和銷燬對象

本章的主題是創建和銷燬對象:何時以及如何創建對象,何時以及如何避免創建新對象,如何確保它們能夠適時地銷燬,以及如何管理對象銷燬之前必須進行的各種清理動作。

第一條:考慮用靜態工廠方法代替構造器

類通過提供一個公有的靜態工廠方法來返回一個實例對象,而不是通過構造器,有以下優勢:

1、靜態工廠方法與構造器相比的第一大優勢是有名稱

我們知道在一些類中會出現包含眾多參數的構造器,這些構造器的參數使我們在使用的時候非常容易出現迷惑,用錯參數之類的。而通過靜態工廠方法,我們可以慎重選擇比較區別度的名稱來定義,這樣就可以避免這種問題。

2、靜態工廠方法可以在每次調用它們時不必都創建一個新對象

通過靜態工廠方法,我們可以使用預先構建好的實例,或將構建好的實例緩存起來進行重複利用,從而避免不必要的對象創建,這點的思想跟享元模式很像,如果針對創建過程比較複雜的對象,這樣使用緩存能極大提高效率。

3、靜態工廠方法可以返回原返回類型的任何子類型對象

靜態工廠方法可以選擇返回對象的類型,這樣就對我們實現API的隱藏有很好的靈活性,非常適用於基於接口的框架中。

4、靜態工廠方法創建參數化類型實例的時候,使代碼變得簡潔

第二條:遇到多個構造器參數時要考慮使用建造器

在設計模式一欄中,我們介紹了建造者模式,這裡的第二條就是針對多參數創建對象的情況建議使用建造器模式。

Effective Java——第二章,創建和銷燬對象

第三條:用私有構造器或者枚舉類型強化Singleton屬性。

本條規則描述的就是單例模式的實現方式,建議使用枚舉類型創建單例。

Effective Java——第二章,創建和銷燬對象

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

比如在我們實際開發過程中,所創建的很多工具類,這些工具類中一般都包含很多static靜態方法,就不需要new一個對象,然後調用。這裡我們就可以通過私有構造函數來防止實例化。

Effective Java——第二章,創建和銷燬對象

第五條:避免創建不必要的對象

儘量使用靜態工廠方法而不是構造器來創建對象,重用那些不可修改的的對象。比如下面的一個反例:

Effective Java——第二章,創建和銷燬對象

在上面的例子中,isBabyBoomer每次調用的時候都會新建一個Calendar、兩個Date對象、一個TimeZone對象。這是非常不必要的,優化後的如下:

Effective Java——第二章,創建和銷燬對象

通過引入靜態代碼塊保證了只有在類第一次加載的時候創建這些實例,而不是每次調用isBabyBoomer方法都創建這些實例。改進後的形式對於頻繁調用的方法效率提高會很多,但是如果方法永不被調用,就沒必要初始化常量了。這裡可以採用延時初始化策略,在需要的時候進行初始化。

這裡補充一個概念自動裝箱,在JDK1.5中允許程序員將基本數據類型和裝箱類型混用,按需要自動裝箱和拆箱,自動裝箱使得基本類型和裝箱類型關係越來越模糊,但是效率遠不及基本類型。所以要警惕無意識的自動裝箱,優先使用基本數據類型。

第六條:清除過期的對象引用

我們都知道Java程序一般不需要用戶主動進行垃圾回收處理,但是這並不代表我們不需要思索垃圾回收內存管理的事情。其實不然,比如下面的例子:

Effective Java——第二章,創建和銷燬對象

在這個例子中,並沒有明顯的錯誤。不嚴格的講,這段程序有一個“內存洩漏”,體現在一個棧先增長在收縮,那麼從棧中彈出來的對象是不會主動被垃圾回收器回收,這是因為棧中維護著這些過期對象的過期引用,過期應用指不會被解除的引用。在本例中,所有elements數組的活動部分之外都是引用過期的。一般這類問題通過賦值null來清楚。

Effective Java——第二章,創建和銷燬對象

所以在工作中我們要警惕內存洩漏:

  • 只要類是自己管理內存,我們就要注意內存洩漏

  • 注意緩存是否有內存洩漏

  • 注意第三方的監聽器和其它回調,這裡可以使用弱引用。

總結

在本章中講解了到很多避免創建新對象的開發技巧,但是並不是意味著“永遠不要創建新對象,創建新對象的代價很大”,相反,對於小對象的構造器只是做了很多少量顯示工作,所以針對耗費小的可以進行創建一些新對象,提升程序的清晰性、簡潔性。

反之通過自己的對象池(Object Pool)來避免維護新對象的創建並不可可取,除非針對非常重量級的對象創建。正在正確使用對象池的經典案例就是數據庫的連接池,建立數據庫的連接代價非常昂貴,隱刺重用對象很重要。如果無腦使用,肯定會增加內存佔用,代碼也會多很多邏輯,現在的JVM具有高度優化的垃圾回收器,其性能很容易超過輕量級對象的對象池。


分享到:


相關文章: