天天看点

Singleton 单例模式的三种实现

一、什么是Singleton?

Singleton 指的是仅仅只能被实例化一次的类。(通常用来代表本质上唯一的系统组件:窗口管理器,文件系统)

二、Java 1.5 之前的两种实现方法

构造器保持为私有的

,然后导出

公有的静态成员

public static

),允许客户端访问这个类的唯一的实例。私有的构造器(

private Dog(){...}

)仅会被调用一次去进行实例化,这保证了类只能被实例化一次;

  1. 公有的静态成员是一个

    final域

    (调用:

    Dog.INSTANCE

//Singleton with public final field
public class Dog {
	//公有静态实例(final域保持实例唯一)
	public static final Dog INSTANCE = new Dog();
	//私有的构造器
	private Dog() {...}
	public void sleep() {...}
}
           
  1. 公有的静态成员是一个

    静态工厂方法

    (调用:

    Dog.getInstance

//Singleton with static factory
public class Dog {
	//公有静态实例(final域保持实例唯一)
	public static final Dog INSTANCE = new Dog();
	//私有的构造器
	private Dog() {...}
	//静态方法
	public static Dog getInstance() {return INSTANCE;}
	public void sleep() {...}
	
	//readResolve方法,保证signleton的实现
	private Object readResolve() {
		//返回真实例,并让垃圾回收器处理假实例
		return INSTANCE;
	}
}
           

一般都会使用第二种方法,调用静态方法

Dog.getInstance

,每次调用都会返回同一个对象引用,所以保持了实例的唯一性。

注意:

  • public 和 private 的构造器可以多次调用;
  • 享有特权的客户端可以借助

    AccessibleObject.setAccessible

    方法,通过反射机制来调用私有构造器。(抵御方法:修改构造器,在第二次被调用时抛异常)
  • 为了实现序列化,只implement Serializabled是不够的,还得声明所有实例域都是瞬时(transent,文末有解释)的,并在Dog类里提供一个

    readResolve

    方法。否则,每次反序列化的时候,都会新创建一个实例(假冒的Dog类)

三、java 1.5之后 —— 最佳实现方法(enum)

  1. 编写一个包含

    单个元素的枚举类型enum

    ,而不是class类
//Enum singleton - the preferred approach
public enum Dog {
	INSTANCE;
	public void sleep () {...}
}
           

这种方法在功能上与公有域方法相近,但更加简洁,提供了

序列化机制

,即使面对复杂的序列化或者发射攻击,也绝对防止了多次实例化。

瞬时(transient):

一个持久化类的实例可能处于三种不同的状态中的某一种。这三种状态的定义则与所谓的持久化上下文(persistence context)有关。Hibernate的Session对象就是这个所谓的持久化上下文。

瞬态(transient)

该实例从未与任何持久化上下文关联过。它没有持久化标识(相当于主键值)。
           

持久化(persistent)

实例目前与某个持久化上下文有关联。它拥有持久化标识(相当于主键值),并且可能在数据库中有一个对应的行。对于某一特定的持久化上下文,Hibernate保证持久化标识与Java标识(其值代表对象在内存中的位置)等价。
           

托管(detached)

实例曾经与某个持久化上下文发生过关联,不过那个上下文被关闭了,或者这个实例是被序列化(serialize)到另外的进程。它拥有持久化标识,并且在数据库中可能存在一个对应的行。对于托管状态的实例,Hibernate不保证任何持久化标识和Java标识的关系。
           

参考1、《Effictive Java》