第3条:用私有构造器或者枚举类型强化Singleton属性

术语:

Singleton:指仅仅被实例化一次的类。


        Singleton会使它的客户端测试变得十分困难,因为无法给Singleton替换模拟实现,除非它实现一个充当其类型的接口。

        实现Singleton有以下三种方法:

1、实现公有静态成员函数,并将之设置为final。例如

// Singleton with public field
public class Elvis {
	public static final Elvis INSTANCE = new Elvis();
	private Elvis() { ... }
	
	public void leaveTheBuilding() { ... }
}
        这种方法把构造函数私有化,这样保证了Elvis的全局唯一性。但有一个问题,就是享有特权的客户端可能借助AccessibleObject.setAccessible方法,通过反射机制调用私有构造器,这样就破坏了Singleton,一种补救的办法是,修改构造器让他被要求生成第二个实例的时候抛出异常。

2、实例私有化,但通过公有的静态方法返回,例如

public class Elvis {
	private static final Elvis INSTANCE = new Elvis();
	private Elvis() { ... }
	public static Elvis getInstance() { return INSTANCE;  }
	
	public void leaveTheBuilding() { ... }
}
        对于静态方法Elvis.getInstance的所有调用都会返回同一个对象的引用,这样就保证了唯一性,但是还是存在第1种方法里的反射机制进行攻击的问题。

        第一种方法的好处在于组成类的成员的声明很清楚的表明了这是类的一个Singleton,但是相比之下不再比第二种方法有什么优势,因为JVM几乎都将静态工厂方法的调用内联化。第二种方法的优势之一在于,它提供了灵活性,在不改变其API的前提下,可以改变该类是否应该是Singleton的想法,这种方法可以很容易的被修改成按不同需求返回相应的唯一实例。第二个优势在于与泛型有关,但是相比之下,第一种方法更为简单。

        以上两种方法还有一个问题,就是在实现Singleton类序列化的时候,仅仅在声名中加上“implement Serializable”是不够的。为了维护并保证Singleton,必须声名所有的实例都是瞬时的(transient),并提供一个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、包含单个元素的枚举类型。

public enum Elvis {
	INSTANCE;
	
	public void leaveTheBuilding() { ... }
}
        这种方法更加简洁,无偿的提供了序列化机制,绝对防止多次实例化(由Enum保证),即使是在面对复杂的序列化或者反序列化或者反射攻击的时候也可以保证唯一。这已成为实现 Singleton的最佳方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值