Effective Java Item4 通过私有的构造方法来阻止类的实例化

有时,你想要编写一个只包含一组静态方法和静态字段的类。这种类有一个不太好的名声,因为有些人会滥用他们,不从对象的角度来思考,而是坚信他们的想法是正确无误的 。他们可以把原生类型的值或数组相关的方法分一组,就像java.lang.Mathjava.util.Arrays的方式。还可以将静态方法划分到一起,包括工厂(Item 1),用于实现了某个接口的对象,就想java.util.Collections一样。(从Java 8开始,如果想要自己修改,那么你还可以将这类方法放到接口中)最后,还可以将针对终态类的方法划分到一起,因为你无法再将他们放到子类中了 。

把这样的公共类设计成不可实例化,是因为它的实例化是没有意义地。然而,在没有显式构造函数的情况下,编译器提供一个公共的、无参数的默认构造函数。对使用者来说,这个构造函数跟其它的没什么区别。我们常常会在已发布的APIs中看到无意中被实例化的类 。

通过将一个类设置为抽象类来强制禁止类的实例化是行不通的。这个抽象类可以有子类,然后子类可以实例化。而且这会误导使用者去想要去设计一个类,然后继承这个抽象类(Item 19)。然后这里有一个简单的做法来保证非实例化。只有在类不包含显示构造函数时候,默认的构造函数才会生成。所以,一个类可以通过提供私有的构造函数来做到非实例化。

1
2
3
4
5
6
7
8
// Noninstantiable utility class
public class UtilityClass {
// Suppress default constructor for noninstantiability
private UtilityClass() {
throw new AssertionError();
}
... // Remainder omitted
}

因为显示指定构造函数是私有的,所有无法在类外面访问到。AssertionError不是必须的,但它提供了一个保障,防止构造函数在类的内部被意外调用。它保证了类在任何情况都不会被实例化。这种做法有点反常识,因为提供构造方法的目的仅仅是为了自己不能被调用 。所以,更好的做法则是加上一些注释说明,正如上述代码所做的那样。

这样做的副作用是,阻止了类被子类化。所有构造函数都必须显式或隐式地调用父类构造函数,但是子类将没有可访问的父类构造函数来调用。