Java中Singleton的三种实现方式解析

1、什么是Singleton?

《设计模式》的做者、Eclipse和 Junit 的开发者 Erich Gamma 在它的理论体系中将 Singleton 定义为仅仅被实例化一次的类。在当今面向对象程序的实际开发中,Singleton 一般被用来表明一个无状态的对象,例如函数和那些本质上惟一的系统组件。设计模式

值得注意的是,使类成为 Singleton 会使得它的客户端测试变得很是困难,由于咱们不可能给Singleton替换模拟实现,除非咱们实现一个充当其类型的接口。app

实现 Singleton 有三种常见方法,他们或是保持构造器私有并导出公有的静态成员,或是声明一个包含单个元素的枚举类型。函数

2、Singleton实现 —— 构造器私有

一、公有静态成员为一个final域

//Singleton with public final field 
public class Elvis { 
	public static final Elvis INSTANCE = new Elvis(); 
	pritvate Elvis() { ... } 
	public void leaveTheBuilding() { ... }
}

在这个类中,咱们仅仅拥有一个私有的构造器,它也只在初始化final域时被调用一次。因为缺乏可使用的构造器,后续的程序没法再建立 Elvis 对象。这保证了在该Java程序的整个生命周期中, Elvis 对象有且只有一个存在。测试

但须要注意的是,一些高权限的客户端能够借助 AccessibleObject.setAccessible 方法经过反射机制调用私有的构造器。为了不这样的可能的攻击,能够修改构造器,让它在被要求建立第二个实例的时候抛出异常。ui

公有域方法的主要优点在于,API很清楚地代表了这个类是一个 Singleton ,毕竟这是一个公有的静态属性。另外,这个方法要更加简单。线程

二、公有静态成员为一个静态工厂方法

//Singleton with static factory
public class Elvis { 
	private static final Elvis INSTANCE = new Elvis(); 
	pritvate Elvis() { ... } 
	public static Elvis getInstance(){ return INSTANCE; }
	public void leaveTheBuilding(){ ... }
}

显然,不管怎样调用 getInstance 方法,返回的都是同一个对象的引用。注意上面提示的反射攻击问题依然存在。设计

静态工厂方法有两大优点code

  • 第一,它提供了更多的灵活性,在不改变API的前提下,咱们能够轻易地自由调整这个类是不是Singleton。工厂方法返回该类的惟一实例,但它很容易修改为别的样子,例如为每一个调用该方法的线程提供惟一实例。
  • 第二,若是程序须要,咱们能够编写一个泛型 Singleton 工厂。
  • 第三,咱们能够经过方法引用做为提供者,好比 Elvis::instance 就是一个 Supplier< Elvis >

(注:方法引用是Java8的一个新特性)对象

除非咱们须要上述的其中一种优点,咱们仍是应该选择更简单易懂的使用公有域的方法。接口

三、将利用上述方法实现的Singleton类变为可序列化的

使用上述两种方法实现的 Singleton ,要把他们变成可序列化的,不能仅仅在声明中加上 implements Serializable 。为了维护并保证 Singleton ,咱们必须生命全部实例域都是瞬时的,并提供一个 readResolve 方法。不然在咱们每次序列化时都会建立一个新的实例。为了防止这种状况,咱们要在 Elvis 类中加入以下这样的 readResolve 方法。

//readResolve method to preserve singleton property 
	private Object readResolve(){
		//Return the one true Elvis and let the garbage collector take care of the Elvis impersonator
		return INSTANCE;
	}

3、Singleton实现 —— 声明包含单个元素的枚举类型

//Enum singleton - the preferred approach
public enum Elvis{
	INSTANCE;
	public void leaveTheBuilding(){ ... }
}

这种方法在功能上与公有域方法类似,但更加简洁,无偿地提供了序列化机制,绝对防止屡次实例化,即便是在面对复杂的序列化或者反射攻击的时候。 虽然这种方法尚未广 泛采用,可是单元素的枚举类型常常成为实现 Singleton 的最佳方法。 注意,若是 Singleton 必须扩展一个超类,而不是扩展 Enum 的时候,则不宜使用这个方法(虽然能够声明枚举去实现接口)。

相关文章
相关标签/搜索