第7條 避免使用終結方法

一橱健、終結方法VS析構器

熟悉C++的都知道斯撮,析構器是用來回收一個對象所占用資源的常規(guī)方法批幌,是構造器所必需的對應物础锐。在JAVA中,當一個對象變得不可達時荧缘,垃圾回收器會回收與該對象關聯(lián)的存儲空間皆警,這不需要我們操心。對于非內存資源的回收截粗,C++析構器是可以管理的信姓,而JAVA的垃圾回收器是不會管理這些非內存資源的,我們通常使用try-finally塊來顯式管理這些資源绸罗,比如文件操作意推,socket連接等。

二珊蟀、終結方法的缺點

1菊值、行為不穩(wěn)定

終結方法不能保證被及時地執(zhí)行,有時甚至根本不會被執(zhí)行。從一個對象變成不可到達開始腻窒,到它的終結方法被執(zhí)行昵宇,所花費的時間是任意長的。如果在終結方法中執(zhí)行文件關閉操作儿子,很有可能造成程序因為不能打開文件而出現(xiàn)運行錯誤瓦哎,因為終結方法執(zhí)行時間是任意的,而系統(tǒng)文件打開數(shù)是一定的典徊,當系統(tǒng)文件打開數(shù)達到最大時杭煎,程序就會拋出Open too many files異常,這是系統(tǒng)級錯誤卒落,所以不能依靠終結方法來處理這些有限資源的釋放羡铲。另外,也不能依賴終結方法來更新重要的持久狀態(tài)儡毕。例如也切,依賴終結方法解放共享資源(比如數(shù)據(jù)庫)上的永久鎖,很容易讓整個分布式系統(tǒng)垮掉腰湾。

ps:可達性
從強到弱雷恃,不同級別的可達性反應對象的生命周期,定義如下:

如果一個對象可以被一些線程直接使用而不用通過其他引用對象费坊,那么它就是強可達倒槐。一個新創(chuàng)建的對象對創(chuàng)建它的線程來講就是強可達的。

如果一個對象沒有強可達性附井,但是它可以通過一個軟引用(soft reference.)來使用讨越,那么它就具有軟可達性。

如果一個對象既沒有強可達性永毅,也沒有軟可達性把跨,但是它可以通過一個弱引用(weak reference)來使用,那么他就具有弱可達性沼死。當弱引用指向的弱可達對象沒有其他的引用着逐,那么這個對象就會被回收。

如果一個對象既沒有強可達性意蛀,也沒有軟可達性耸别、弱可達性,他已經被finalized县钥,并且有一些虛引用(phantom reference)指向它太雨,那么它就具有虛可達性。

當一個對象不能通過以上的方式指向魁蒜,那么這個對象就變得不可達,并因此適合被回收

2、可移植性問題

及時執(zhí)行終結方法是垃圾回收算法的一個主要功能兜看,而每個JVM實現(xiàn)中锥咸,垃圾回收算法是不一樣的,這就導致終結方法在不同JVM實現(xiàn)下表現(xiàn)的差異细移,這種差異可能帶來災難性影響搏予。

3、性能損失

使用終結方法銷毀對象需要依賴于JVM的垃圾回收算法調度弧轧,這樣必將造成銷毀對象耗時更多雪侥,降低性能。數(shù)據(jù)統(tǒng)計精绎,創(chuàng)建和銷毀一個簡單對象的時間大約為5.6ns速缨,增加一個終結方法使時間增加到2400ns。換句話說代乃,用終結方法創(chuàng)建和銷毀對象慢了大約430倍旬牲。

三、有沒有替代方法?

如果類的對象中封裝的資源確實需要終止搁吓,則可以用下面的方法來替代使用終結方法原茅。只需提供一個顯示的終止方法,并且要求該類的客戶端在每個實例不再有用的時候調用這個方法堕仔。值得提及的一個細節(jié)是擂橘,該實例必須記錄下自己是否已經被終止并了:顯示的終止方法必須在一個私有域中記錄下”該實例不再有效”,可以是一個boolean也可以是其他的摩骨。這樣做為了防止其他方法或者線程來調用這個已經被終結的對象之后造成不可預知的結果通贞。顯示終結方法的典型例子是InputStream、OutputStream和java.sql.Connection上的close方法仿吞。顯示的終止方法通常與try-finally結構結合以來使用滑频,確保及時終止。在finally子句內部調用顯示的終止方法唤冈,可以保證即使在使用對象的時候有異常拋出峡迷,終止方法也會進行的:

public void test() {
FileInputStream fin = null;
try {
fin = new FileInputStream(filename);
//do something.
} finally {
fin.close();
}
}

終結方法有什么好處?

它們有兩種合法的用途。第一種用途是你虹,當對象的所有者忘記調用前面段落中建議的顯示終結方法時绘搞,可以充當“安全網”的作用,做好第二道防御的措施傅物。雖然這樣做并不能保證會及時的調用夯辖,但是遲一點執(zhí)行總比沒有執(zhí)行好吧

終結方法第二種合理的用途與對象的本地對等體(native peer)有關。本地對等體是一個本地對象(native object),普通對象通過本地方法委托給一個本地對象董饰。因為本地對等體不是一個普通的Java對象蒿褂,所以垃圾回收器并不會知道它圆米,當它的Java對等體被回收的時候,它不會被回收啄栓。在本地對等體并不擁有關鍵資源的前提下娄帖,終結方法正是執(zhí)行這項任務最合適的工具。如果本地對等體擁有必須被馬上終止的資源昙楚,那么該類就應該有一個顯示的終止方法近速。

值得注意的是,“終結方法鏈”并不會被自動的執(zhí)行堪旧。如果類有終結方法削葱,并且子類覆蓋了終結方法,則需要手動的去調用超類的終結方法淳梦∥鲈遥可以這么使用來確保父類的終結方法也會得到調用:

@Override
protected void finalize() throws Throwable {
try {
// .....
} finally {
super.finalize();
}
}
如果子類覆蓋了超類的終結方法,但是忘了手動的調用終結方法谭跨,那么超類的終結方法將永遠也不會被調用到干厚。要防范這樣粗心大意或者惡意的子類也是有可能的,代價就是為每個將被終結的對象創(chuàng)建一個附加的對象螃宙。不是把終結方法放在要求終結處理的類中蛮瞄,而是放在一個匿名的類中,該匿名類的唯一用途就是終結它的外圍實例(enclosing instance)谆扎。該匿名類的單個實例被成為終結方法守衛(wèi)者(finalizer guardian)挂捅,外圍類的每個實例都會創(chuàng)建這樣一個守衛(wèi)者。外圍實例在私有實例域中保存著一個對終結方法守衛(wèi)者的唯一引用堂湖,因此終結方法守衛(wèi)者與外圍實例可以同時啟動終結過程闲先。當守衛(wèi)者被終結的時候,它執(zhí)行外圍實例所期望的終結行為无蜂,就好像它是終結方法外圍讓對象上的一個方法一樣伺糠。

public class Parent {

public static void main(String[] args){
doSth();
System.gc();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

private static void doSth() {
Child c = new Child();
System.out.println(c);
}

private final Object guardian = new Object(){

@Override
protected void finalize(){
System.out.println("執(zhí)行父類中匿名內部類--終結方法守衛(wèi)者,重寫的finalize()");
// 在這里調用Parent重寫的finalize即可在清理子類時調用父類自己的清理方法
parentlFinalize();
}
};

protected void parentlFinalize() {
System.out.println("執(zhí)行父類自身的終結方法");
}
}

class Child extends Parent {

@Override
protected void finalize() {
System.out.println("執(zhí)行子類finalize方法,注意斥季,這里子類并沒有調用super.finalize()");
// 由于子類(忘記或者其他原因)沒有調用super.finalize()
// 使用終結方法守衛(wèi)者可以保證子類執(zhí)行finalize()時(沒有調用super.finalize())训桶,父類的清理方法仍舊調用
}
}

總之除非是作為資源回收處理的第二道防線(安全網)或者是為了終結非關鍵的資源,否則請不要使用終結方法酣倾。如果沒辦法真的使用了finalize舵揭,別忘記了調用super.finalize()。還應考慮是否使用終結方法守衛(wèi)者躁锡,使未調用super.finalize()方法的類的父類的終結方法也會被執(zhí)行午绳。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末区赵,一起剝皮案震驚了整個濱河市杜跷,隨后出現(xiàn)的幾起案子擦剑,更是在濱河造成了極大的恐慌浑测,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耕漱,死亡現(xiàn)場離奇詭異算色,居然都是意外死亡,警方通過查閱死者的電腦和手機螟够,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來峡钓,“玉大人妓笙,你說我怎么就攤上這事∧苎遥” “怎么了寞宫?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拉鹃。 經常有香客問我辈赋,道長,這世上最難降的妖魔是什么膏燕? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任钥屈,我火速辦了婚禮,結果婚禮上坝辫,老公的妹妹穿的比我還像新娘篷就。我一直安慰自己,他們只是感情好近忙,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布竭业。 她就那樣靜靜地躺著,像睡著了一般及舍。 火紅的嫁衣襯著肌膚如雪未辆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天锯玛,我揣著相機與錄音咐柜,去河邊找鬼。 笑死更振,一個胖子當著我的面吹牛炕桨,可吹牛的內容都是我干的。 我是一名探鬼主播肯腕,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼献宫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了实撒?” 一聲冷哼從身側響起姊途,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤涉瘾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后捷兰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體立叛,經...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年贡茅,在試婚紗的時候發(fā)現(xiàn)自己被綠了秘蛇。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡顶考,死狀恐怖赁还,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情驹沿,我是刑警寧澤艘策,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站渊季,受9級特大地震影響朋蔫,放射性物質發(fā)生泄漏。R本人自食惡果不足惜却汉,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一驯妄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧病涨,春花似錦富玷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至幻工,卻和暖如春励两,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背囊颅。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工当悔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人踢代。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓盲憎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胳挎。 傳聞我的和親對象是個殘疾皇子饼疙,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內容

  • 一. finalize()基本概念 所謂的終結方法其實是指finalize()。終結方法finalizer通常是不...
    Ruheng閱讀 3,019評論 3 3
  • 終結方法的缺點 終結方法(finalizer)是不可預測的慕爬,也是很危險的窑眯。使用終結方法會導致行為不穩(wěn)定屏积、降低性能,...
    每天學點編程閱讀 364評論 0 1
  • 1. Java基礎部分 基礎部分的順序:基本語法磅甩,類相關的語法炊林,內部類的語法,繼承相關的語法卷要,異常的語法渣聚,線程的語...
    子非魚_t_閱讀 31,623評論 18 399
  • 閱讀經典——《Effective Java》03 Java語言包中的Object類是所有類的祖先。該類提供了諸如e...
    金戈大王閱讀 3,134評論 19 10
  • 原文閱讀 前言 這段時間懈怠了却妨,罪過饵逐! 最近看到有同事也開始用上了微信公眾號寫博客了,挺好的~給他們點贊彪标,這博客我...
    碼農戲碼閱讀 5,961評論 2 31