一、什么是Singleton?
Singleton 指的是仅仅只能被实例化一次的类。(通常用来代表本质上唯一的系统组件:窗口管理器,文件系统)
二、Java 1.5 之前的两种实现方法
把
构造器保持为私有的
,然后导出
公有的静态成员
(
public static
),允许客户端访问这个类的唯一的实例。私有的构造器(
private Dog(){...}
)仅会被调用一次去进行实例化,这保证了类只能被实例化一次;
- 公有的静态成员是一个
(调用:final域
)Dog.INSTANCE
//Singleton with public final field
public class Dog {
//公有静态实例(final域保持实例唯一)
public static final Dog INSTANCE = new Dog();
//私有的构造器
private Dog() {...}
public void sleep() {...}
}
- 公有的静态成员是一个
(调用:静态工厂方法
)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类里提供一个
方法。否则,每次反序列化的时候,都会新创建一个实例(假冒的Dog类)readResolve
三、java 1.5之后 —— 最佳实现方法(enum)
- 编写一个包含
,而不是class类单个元素的枚举类型enum
//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》