Effective Java—04 使用私有构造器执行非实例化
背景
试想一下,你创建了一个工具类,工具类的所有成员都是静态的以便我们可以全局访问(当然工具类的实现方式也可以通过单例来实现,此处我们不讨论这种方式),我们还有必要提供构造方法吗,或者说构造方法的意义在哪,自然是没有的,还会占用资源!所以我们不希望外部通过调用构造方法创建实例来访问工具类中的成员,大家肯定会想到将构造方法私有化。这就是非实例化:不用实例化的类。(实例化没有任何意义,此时这个类我们希望它是非实例化的)
实现
public class Utils {
public static final int count = 100;
private Utils(){
//避免不小心在类的内部调用构造器
throw new AssertionError();
}
public static final void doSomething(){
System.out.println("do something...");
}
}
在看到这里的时候有疑惑为什么要抛出AssertionError,是只能抛出AssertionError还是AssertionError在这里更合适,可以看到源码中注释的描述:
它在这里抛出,表示断言失败,是一个系统级错误;表示“不应该调用构造器"声明已被违反。
Java中已经为我们提供了许多的工具类,它们有保证非实例化吗?显而易见是有的!
提问:可以使用接口或者抽象类吗?
在JDK1.8之后,接口和抽象类中可以存在静态成员,我们也可以通过 java.util.Collections
的方式,把实现特定接口上面的静态方法进行分组,也包括工厂方法。最后,这样的类可以用于在 final 类上对方法进行分组,因为不能将它们放在子类中。
抽象类虽然不能直接实例化,但是其最终会被子类化,子类是可以实例化的;此外,它误导用户认为该类是为继承而设计的。
总结
因为显式构造方法是私有的,所以在类之外是不可访问的。AssertionError异常不是严格要求的,但是它提供了一种保证,以防在类中意外地调用构造方法。它保证类在任何情况下都不会被实例化。这个习惯用法有点违反直觉,好像构造方法就是设计成不能调用的一样。因此,如前面所示,添加注释是种明智的做法。
这种习惯有一个副作用,阻止了类的子类化。所有的构造方法都必须显式或隐式地调用父类构造方法,而子类则没有可访问的父类构造方法来调用。