本條的意思是,讓你儘可能不要在你的類中覆蓋finalize方法,而後在在裏面寫一些釋放你的類中資源的語句。java
至於爲何要避免覆蓋並使用finalize呢,理由以下:安全
(1)finalize方法不能保證它能被及時的執行。
(2)finalize方法甚至都不會被執行。
(3)System.gc和System.runFinalization這兩個方法只是能增長finalize方法被調用的概率。
(4)惟一能保證finalize方法被執行的方法有兩個,System.runFinalizersOnExitRuntime.runFinalizersOnExit可是這兩個方法已經被棄用。
(5)覆蓋並使用終結方法會形成嚴重的性能損失。(在機器上,建立和銷燬一個簡單對象時間大約5.6ns,二增長一個終結方法使時間增長到了2400ns,慢了約430倍)。ide
那麼,若是類中的資源確實須要被釋放,咱們應該如何操做呢?通常來講,須要釋放的資源有線程或者文件還有涉及到本地的資源的對象。咱們只須要提供一個顯示的終止方法,用來釋放資源,並要求這類的使用者在再也不使用這個類的時候調用這個方法,而且在類中添加一個標誌,來標記資源是否已經釋放,若是已經被釋放了,那這個類中的方法若是在被調用的話就拋出IllegalStateException異常,一個很好的例子就是InputStream和OutputStream。通常在調用咱們本身定義的public修飾的終止方法的時候最好和try—finally一塊兒使用,就像下面這樣:性能
class MyObject{ private boolean isClosed = false; //public修飾的終止方法 public void close(){ //資源釋放操做 ... isClosed = true; } } public static void main(String... args) { MyObject object = new MyObject(); try{ //在這裏面使用object; ... } finally { //在這裏面關閉object; object.close(); } }
至於何時使用終結方法纔是比較合理的呢?當子類覆蓋了超類的終結方法,但忘記手工調用超類的終結方法或有意不調用,那麼超類的終結方法將永遠不會被調用,要防範這樣的粗枝大葉或惡意的子類,只時候咱們可使用下面方式spa
(1)用終結方法充當「安全網」線程
「安全網」的做用是當咱們提供的public修飾的終結方法被在外部忘記調用的時候提供一種安全保障。咱們能夠看下FileInputStreamcode
public void close() throws IOException { synchronized (closeLock) { if (closed) { return; } closed = true; } if (channel != null) { channel.close(); } fd.closeAll(new Closeable() { public void close() throws IOException { close0(); } }); } protected void finalize() throws IOException { if ((fd != null) && (fd != FileDescriptor.in)) { /* if fd is shared, the references in FileDescriptor * will ensure that finalizer is only called when * safe to do so. All references using the fd have * become unreachable. We can call close() */ close(); } }
能夠看到FileInputStream仍是有覆蓋finalize方法的,而裏面作的就是調用close方法,這是爲了當對象持有者忘記調用close方法,在finalize方法中爲它作調用close的事,這就是「安全網」的意思。對象
(2)終結方法的守衛者。把終結方法放在一個匿名的類,該匿名類的惟一用途就是終結它的外圍實例。外圍實例持有對終結方法守衛者的惟一實例,這意味着當外圍實例是不可達時,這個終結方法守衛者也是不可達的了,垃圾回收器回收外圍實例的同時也會回收終結方法守衛者的實例,而終結方法守衛者的finalize方法就把外圍實例的資源釋放掉,就好像是終結方法是外圍實例的一個方法同樣。來看看java.util.Timer的終結方法守衛者:blog
public void cancel() { synchronized(queue) { thread.newTasksMayBeScheduled = false; queue.clear(); queue.notify(); // In case queue was already empty. } } private final Object threadReaper = new Object() { protected void finalize() throws Throwable { synchronized(queue) { thread.newTasksMayBeScheduled = false; queue.notify(); // In case queue is empty. } } };
cancel方法是Timer提供的顯式終止方法,threadReaper是一個私有變量,保證除了實例自己外,沒有對它的引用,它覆蓋finalize方法,實現與cancel方法同樣的功能。咱們在看個簡單點的例子來加深下理解ip
class A { @SuppressWarnings("unused") //終結守衛者 private final Object finalizerGuardian = new Object() { @Override //終結守衛者的終結方法將被執行 protected void finalize() { System.out.println("A finalize by the finalizerGuardian"); } }; @Override //因爲終結方法被子類覆蓋,該終結方法並不會被執行 protected void finalize() { System.out.println("A finalize by the finalize method"); } public static void main(String[] args) throws Exception { B b = new B(); b = null; System.gc(); Thread.sleep(500); } } class B extends A { @Override public void finalize() { System.out.println("B finalize by the finalize method"); } }
運行的結果是:
B finalize by the finalize method
A finalize by the finalizerGuardian
最後,在使用終結方法的時候咱們要注意什麼?其實很簡單,只須要注意確保super.finalize()方法必定會被執行。
確保它必定會被執行的方式有兩種: (1)使用try-finally(像上面的安全網同樣); (2)使用「終結方法守衛」