避免使用finalizer

Java语言规范里没有要求finalizer何时被执行,甚至没要求它一定要被执行。各种不同的jvm实现完全可能以它们自己的方式来处理finalizer,所以finalizer里的代码执行没有任何保障,从这点上来说,很多c++程序员把finalizer作为c++析构函数在Java中的对应功能是完全错误的。如果错误地依赖finalizer来释放被占用的资源或者执行某些时序任务,很可能会造成不可预料的错误行为。

Java面试里面一个烂大街的问题就是System.gc()方法有什么作用,其实还有一个类似的方法System.runFinalization(),两者没太大区别,都是向jvm提出gc请求,但根本无法保证jvm会及时作出响应。当然,这两个方法还是提高了finalizer被执行的可能性。

finalizer的另一个问题是异常处理。当其中的代码抛出异常时,如果没有显式定义catch处理,这些异常将被完全忽略,所在线程会瞬间终止,连stack trace都不会打印。

移除过期的对象引用

这节讲的是关于内存泄露的问题。Java有三大主要的内存泄露来源:

1.开发者自己维护的内存

2.缓存

3.listener和callback

第一类:开发者自己维护的内存。这类问题通常是开发者从其它编程语言(例如c++)带来的坏习惯导致,比如下面这段代码:

避免创建不必要的对象

最常见的例子是String,一个小小的String对象看起来不起眼,但在复杂的大型系统中,如果不恰当创建,可能会带来极大的性能开销,比如:

String s = new String("stringette"); // DON'T DO THIS!

这行代码的问题是,作为参数的"stringette"本身已经是所需要的String对象了,菜鸟程序员却调用一个String类的构造函数来再次构造它。这行代码每执行一次,就会在栈里面建立一个一模一样的"stringette"对象。在高频执行的环境下,内存开销会非常大,甚至引起stack overflow。正确的做法是:

又是简单而常用的东西

通过私有构造函数来禁止类被对象化

主要用于各种helper或者factory类,这些类只包含一些工具性的方法,通过静态调用提供服务,完全没必要被对象化。但如果不做特殊处理,编译器会为这些类提供一个默认的无参数构造函数,所以仍然会偶尔被粗心的用户对象化。需要注意的是,把它们定义为抽象类(abstract)并不管用,因为只要定义一个子类,对象化仍然可以完成,而且抽象关键字很容易引起误导,让人觉得这些类就是用来继承的,这就与我们的目的背道而驰了。

正确的方法是定义一个私有的无参数构造函数:

这一节非常简单

用枚举类型来定义Singleton

当然枚举类型从Java5才被引入,版本太旧就不能玩了。

传统的Singleton有两种创建方法,一是final公共对象,即把要作为Singleton的对象定义为public static final,把构造函数定义成private,就完事:

这一节看起来很简单,却还藏着不少好玩的东西。

当构造函数需要很多(4个以上)参数时,优先使用builder设计模式

先说这种情况下传统的做法,一种方法是telescoping constructor(套筒式构造函数?),就用书中的例子:

第6页 共10页