Effective Java(Third Edition) item8:避免使用终结方法(finalizer)和清除方法(cleaner)

注:Java9之前只有终结方法,Java9出现了新的清除方法取代了终结方法。

终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的。清除方法没有终结方法那么危险,但仍然是不可预测、运行缓慢,一般情况下也是不必要的。

talk is cheap,show me the code。

定义一个Room类,实现AutoCloseable接口(使用try-with-resource语句块必须实现这个接口以保证资源被关闭),并实现其中的close()方法,其中还有一个静态内部类State,继承了Runnable接口

import java.lang.ref.Cleaner;

// An autocloseable class using a cleaner as a safety net (Page 32)
public class Room implements AutoCloseable {
    private static final Cleaner cleaner = Cleaner.create();

    // Resource that requires cleaning. Must not refer to Room!
    private static class State implements Runnable {
        int numJunkPiles; // Number of junk piles in this room

        State(int numJunkPiles) {
            this.numJunkPiles = numJunkPiles;
        }

        // Invoked by close method or cleaner
        @Override
        public void run() {
            System.out.println("Cleaning room");
            numJunkPiles = 0;
        }
    }

    // The state of this room, shared with our cleanable
    private final State state;

    // Our cleanable. Cleans the room when it’s eligible for gc
    private final Cleaner.Cleanable cleanable;

    public Room(int numJunkPiles) {
        state = new State(numJunkPiles);
        cleanable = cleaner.register(this, state);
    }

    @Override
    public void close() {
        cleanable.clean();
    }
}

你也许会问,什么时候会调用State中的run()方法呢?以下会这种情况可能会触发:

1、通常是通过调用Room的close方法触发的,后者又调用了Cleanable的清楚方法。如果到了Room实例应该被垃圾回收时,客户端还没有调用close方法,清除方法就会(希望如此)调用State的run方法。

 

写两个测试程序就明白了,先看看第一个测试程序:

// Well-behaved client of resource with cleaner safety-net (Page 33)
public class Adult {
    public static void main(String[] args) {
        try (Room myRoom = new Room(7)) {
            System.out.println("Goodbye");
        }
    }
}

代码中用到了try-with-resource语句块,比try-catch-finally靠谱(至少不会出现异常被覆盖的情况),保证myRoom一定会被回收,打印结果很明显,先是Goodbye,接着输出Cleaning room。作为对比,再看看下面这个糟糕的程序又如何:

// Ill-behaved client of resource with cleaner safety-net (Page 33)
public class Teenager {
    public static void main(String[] args) {
        new Room(99);
        System.out.println("Peace out");

        // Uncomment next line and retest behavior, but note that you MUST NOT depend on this behavior!
//      System.gc();
    }
}

你希望程序运行完了Room对象就回收了,然后输出Cleaning room,但事实是残酷的,仅仅输出了一行Peace out程序就结束了,在我的电脑环境下,只要在Teenager的main方法最后添加System.gc(),也就是把注释去掉,就可以让程序在退出的时候打印出Cleaning room,但是不能保证在你的机器上也能看到相同的行为,因为你根本不知道内存啥时候会被回收!

 

总而言之,除非是作为安全网,或者是为了终止非关键的本地资源,否则请不要使用清除方法,对于Java9之前的发行版本,则尽量不要使用终结方法。若使用了中介方法或者清除方法,则要注意它的不确定性和性能后果。

你可能感兴趣的:(Java,优化技术)