有时,你想编写一个仅仅是一组静态方法和静态域的类。这样的类有个坏名声,因为一些人滥用它们来避免用对象的方式来思考,但是它们确实有正当的用途。以java.lang.Math或者java.util.Arrays的方式,它们可以把在原始或者队列上相应的方法归类。以java.util.Collections的方式,它们也可以归类实现了某个接口的对象的静态方法(包括工厂)(条目1)。(就像在Java 8,你也可以把这样的方法放在接口中,假设接口是你可修改的。)最后,这样的类可以在final类中归类方法,因为你不能够把它们放到子类里面。
效用(utility)类是设计成不可以实例化:一个实例毫无意义。然而,缺少了显示的构造子,编译器提供了一个公开的无参数默认构造子(default constructor)。对一个用户来说,这个构造子和其他的构造子是不可分辨的。在发布的API中,看见意外的可实例化类不是不常见的。
类变为一个抽象类的方法实现不可实例化,这并不奏效。类可以子类化,子类可以实例化。再者,这误导用户认为这个类是为继承而设计(条目19)。然而,有一个简单的惯常方法来保证不可实例化。只要一个类没有显示的构造子,一个默认构造子就会产生,所以类可以通过包含私有构造子使得它不可实例化:
// Noninstantiable utility class
public class UtilityClass {
// Suppress default constructor for noninstantiability
private UtilityClass() {
throw new AssertionError();
}
... // Remainder omitted
}
因为显式构造子是私有的,在类的外面不能够获取。AssertionError严格来说是不必要的,但是它提供了一种保证,以防构造子在类内意外被调用。它保证了类在任何环境下都不可能被实例化。这个惯常用法是非常反直觉的,因为构造子是专门提供的,以便它不能够调用。所以,包涵一个如上所示的注释是明智的。
有个缺陷,这个惯常用法同时阻止了类的子类化。所有的构造子必须显式地或者隐式地调用超类的构造子,而且子类不可获取调用超类的构造子。