用私有构造器或者枚举类型强化Singleton
程序员文章站
2024-01-05 16:00:10
Effective Java第3条中的Singleton的实现。 ......
参考effective java第三版 joshua j. bloch
参与编写jdk的大佬,上次看collections的源码时看见了他的名字,然后翻了翻书,竟然就是他写的!
1.常见的一种:
public class singleton {
private static final singleton instance=new singleton();
private singleton(){ //如果没有判断,可以通过反射使用构造函数创建对象,然后就不是单例了 if (instance!=null){ //throw exception } }
public static singleton getinstance(){ return instance; }
public void dosomething(){ //... } }
通过反射:可以看到singleton的两个实例不是同一个。
class main {
public static void main(string[] args) { testsingleton(); }
private static void testsingleton() {
singleton s1 = singleton.getinstance(); class<singleton> clazz = singleton.class; try { constructor<singleton> constructor = clazz.getdeclaredconstructor(new class[]{}); constructor.setaccessible(true); singleton s2 = constructor.newinstance(new class[]{}); system.out.println(s1 == s2); } catch (nosuchmethodexception e) { e.printstacktrace(); } catch (illegalaccessexception e) { e.printstacktrace(); } catch (instantiationexception e) { e.printstacktrace(); } catch (invocationtargetexception e) { e.printstacktrace(); } } }
2.用枚举:推荐的方法
优点:引用effective java的话:简洁,无偿的提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候。单元素的枚举类常是实现singleton的最佳方法。如果singleton必须扩展一个超类,而不是扩展enum时,不适宜使用这个方法。
public enum enumsingleton {
instance; public void dosomething(){ //... } }
按照第一个测试的时候会报错的。
3.序列化
序列化有个问题就是,反序列化时会创建一个新的实例,破坏单例,下面让原来那个类实现serializable接口。
public class singleton implements serializable { private static final singleton instance=new singleton(); private singleton(){ if (instance!=null){ try { throw new exception("instance已存在!"); } catch (exception e) { e.printstacktrace(); } } } public static singleton getinstance(){ return instance; } public void dosomething(){ //... } }
测试一下:effective java的第9条 使用try-with-resources优于try-finally,关闭资源的时候。
private static void testserializablesingleton() {
file file=new file("singleton.out"); try(objectoutputstream out=new objectoutputstream(new fileoutputstream(file)); objectinputstream in=new objectinputstream(new fileinputstream(file))){ out.writeobject(singleton.getinstance()); singleton singleton= (singleton) in.readobject(); system.out.println(singleton == singleton.getinstance()); } catch (filenotfoundexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } catch (classnotfoundexception e) { e.printstacktrace(); } }
打印的结果是false,说明序列化破化了单例,因为反序列化是反射调用了无参构造函数。
解决方法:在类里加入这个方法,详见effective java第89条
private object readresolve() { return instance; }
然后结果就是true了。