单例就是一个类只会被实例化一次,仅且一次。单例的典型用法是,代表了一些系统组件,他们在本质上是唯一的,比如视屏显示或者文件系统。

单例有俩种实现方式,都是基于私有化构造方法,并提供一个静态的公有成员,来让客户端获取到类的唯一的一个实例。在其中一种方式中,公有静态成员是一个final成员变量:

public class Elvis{
	public static final Elvis INSTANCE = new Elvis();
	private Elvis(){
	}
}

私有构造函数只调用一次,来初始化公有的静态成员变量。没有公有的或者受保护的构造函数确保了一个只有一个Elvis实例存在一旦Elvis类初始化-不多不少。客户端对此无能为力。

在第二种实现方式中,公用的静态方法替换了之前的公有的静态的不可改变的成员变量:

public class Elvis{
	private static final Elvis INSTANCE = new Elvis();
	private Elvis(){}
	public static Elvis getInstance(){
		return INSTANCE;
	}
}

所有的调用Elvis.getInstance的都返回了相同的一个对象引用,不会再创建新的Elvis实例了。

第一种实现方式的主要优点在于组成类的成员变量的声明,让这个类是一个单例这样的情况显而易见:公有静态域是final的,也就是不可修改的,因此它总是包含了同一个对象引用。第一种实现方式也有一些轻微的性能优势,但是一个很好实现的JVM能通过将第二种实现方式的静态工厂方法inline来抵消。

第二种实现方式的主要优势在于提供了便捷性,让你可以改变使用单例的主意而不需要改变API,静态工厂方法返回返回类的唯一实例,但是可以很容易的修改成不同的进程返回不同的唯一实例。

总的来说,如果你绝对的确定这个类将永远的保持单例,那么就是使用第一种实现方式,使用第二种方式,如果你想保留这种修改的可能。

让一个单例类可以实例化,仅仅实现 Serializable接口是不够的。为了保证单例,你必须提供readResolve方法。否则任何一个序列化的实例在反序列化的时候都会新创建一个实例,在这个例子中将导致虚假的单例。为了防止这种情况的发生,添加如下readResolve到Elvis方法中:

private Object readResolve() throws ObjectStreamException{
	return INSTANCE;
}

一个统一的主题成为这个例子和例子21的基础,它描述了一个type-safe enum模式。在俩种情形中私有的构造方法确保了没有相关类的新实例被创建,在初始化之后。在这个例子中,这个类只有一个实例被创建了。在下一个例子中,更进一步:公有构造函数的缺席被使用来确保类没有实例被创建。

Item3 用私有构造函数实现不可实例化

偶然之间,你需要写一个仅仅是包含了静态的方法和静态域的类,这样的类的名声不好,这是因为一些人滥用他们在面向对象的语言中写过程化的程序,但是他们确实有可用的地方。他们可用用来将和基本类型值和数组相关的方法放在一起,像java.lang.Math或者java.util.Arrays那样的形式,或者组合一个实现了特定接口的对象,以java.util.Connections的形式。他们也可以用在final修饰的类上,组合一些方法,这样就可以不用继承这个类了。那些工具类在设计时,就不不允许实例化的,一个实例反而是无意义的。如果没有一个显示的构造函数,编译器会默认加一个公有的构造函数,也就是默认的无参构造函数。对用户来说,这个与其他的构造函数没有区别。在已发布的API中,经常能看到不需要实例化却可以实例化的类。

尝试通过将一个类抽象化(abstract)来实现不可实例化不可行 此类可以被继承然后实例化。更糟的是,这会让用户误解,这个类就是设计用来继承的。这里有一个简单地方式实现不可实例化。仅当类没有显示的构造函数时,编译器才会添加默认的构造函数,因此提供一个显示的私有构造函数就好了。

public class UtilityClass {
	private UtilityClass(){
	//this constructor will never called
	}
}

因为显示的构造函数是私有的,所以在此类之外是不可见的,假设如果构造函数不会在类的内部调用的话,这个类就永远不会被初始化。这个例子稍微有点违反直觉,专门的提供一个构造函数来让他不可被调用。因此提供一个注释来解释你得目的是明智的。

其中的一个副作用就是,这个方式也阻止了类的继承。所有的构造函数都要显示或隐示的调用父类的构造函数,而这个子类却无法调用父类的构造函数。