首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

单例模式以及经过反射和序列化破解单例模式

2013-09-29 
单例模式以及通过反射和序列化破解单例模式对于Java来说单例模式可以有以下几种方式:饿汉方式懒汉方式双重

单例模式以及通过反射和序列化破解单例模式

对于Java来说单例模式可以有以下几种方式:

  1. 饿汉方式
  2. 懒汉方式
  3. 双重检查加锁懒汉方式
  4. 内部类方式
  5. 枚举方式

破解单例模式有两种方式:通过反射的方式和通过序列化的方式。下面将一一对此进行分析。

饿汉方式非常简单,即使用一个初始化的静态变量,代码如下:

1public?class?EagerSingleton {2???private?static?final?EagerSingleton instance =?new?EagerSingleton();3?4???private?EagerSingleton(){}5?6???public?static?EagerSingleton getInstance() {7??????return?instance;8???}9}

懒汉模式和饿汉模式类似,只是静态变量定义时不进行初始化,调用getInstance()时才进行初始化,这就需要考虑多线程时问题,使用synchronized关键字修饰方法即可:

01class?LazySingleton {02????private?static?LazySingleton instance =?null;03????private?LazySingleton() {}04????public?static?synchronized?LazySingleton getInstance() {05????????if?(instance ==?null) {06????????????instance =?new?LazySingleton();07????????}08????????return?instance;09????}10}

懒汉模式实际是一种懒加载,但是为了避免多线程时单例失效,必须对getInstance()方法进行同步。可以使用双重检查方式来避免对方法全部进行加锁:

01class?DoubleCheckSingleton {02????private?static?volatile?DoubleCheckSingleton instance =?null;03????private?DoubleCheckSingleton() {}04????public?static?DoubleCheckSingleton getInstance() {05????????if?(instance ==?null) {06????????????synchronized?(DoubleCheckSingleton.class) {07????????????????if?(instance ==?null) {08????????????????????instance =?new?DoubleCheckSingleton();09????????????????}10????????????}11????????}12????????return?instance;13????}14}

这个需要几个注意点:静态类变量必须声明为private static?volatile,这样可以?写入操作?happens-before?于每一个后续的同一个字段的读操作。在getInstance()方法中,第一次判断null后,使用同步防止多线程时破坏单例。

?

第四种是使用内部类的方式,也是一种懒加载的方式实现:

?

1class?InnerClassLazySingleton {2????private?static?class?SingletonCreator {3????????private?static?final?InnerClassLazySingleton instance =?new?InnerClassLazySingleton();4????}5????public?static?InnerClassLazySingleton getInstance() {6????????return?SingletonCreator.instance;7????}8}

内部类只在第一次调用的时候才会被类加载器加载,实现了懒加载

同时由于instance是static final类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性

但是上面四种方式都可以通过反射的方式来破坏单例:

1InnerClassLazySingleton eagerSingleton = InnerClassLazySingleton.getInstance();2Constructor<InnerClassLazySingleton> constructor = InnerClassLazySingleton.class.getDeclaredConstructor();3constructor.setAccessible(true);4?5InnerClassLazySingleton eagerSingletonFromRef = constructor.newInstance();6System.out.println("使用反射破解饥渴单例模式:"?+ (eagerSingleton == eagerSingletonFromRef ??"否"?:?"是"));

并且如果实现了Serializable接口的话,通过序列化的方式也可以破坏单例:

1ByteArrayOutputStream baos? =?new?ByteArrayOutputStream();2ObjectOutputStream oos =?new?ObjectOutputStream(baos);3oos.writeObject(eagerSingleton);4?5ObjectInputStream ois =?new?ObjectInputStream(new?ByteArrayInputStream(baos.toByteArray()));6InnerClassLazySingleton eagerSingletonFromSerial =? (InnerClassLazySingleton) ois.readObject();7System.out.println("使用序列化破解饥渴单例模式:"?+ (eagerSingleton == eagerSingletonFromSerial ??"否"?:?"是"));

可以通过在单例类中添加readResolve()方法的方式来解决:

01class?EagerSingleton?implements?Serializable {02????private?static?final?EagerSingleton instance =?new?EagerSingleton();03????private?EagerSingleton() {}04????public?static?EagerSingleton getInstance() {05????????return?instance;06????}07?08????private?Object readResolve() {09????????System.out.println("print from readResolve() method");10????????return?getInstance();11????}12?13????private?void?readObject(ObjectInputStream ois)?throws?IOException, ClassNotFoundException {14????????System.out.println("print from readObject() method");15????????ois.defaultReadObject();16????}17}

readResolve()方法是用来替换从流中读取的对象的,它在readObject(ObjectInputStream)方法之后被调用,readObject()方法即从流中读取对象的方法。这样就可以避免使用序列化方式破坏单例。

?

但是上面的四种方式还是无法避免反射的方式来破坏单例的情况,可以使用枚举的方式实现单例:

1enum?EnumSingleton {2????INSTANCE;3?????4????public?static?void?method() {5?????????6????}7}

通过反射方式创建实例时会抛出?Exception in thread "main" java.lang.NoSuchMethodException: net.local.singleton.EnumSingleton.<init>()?异常。并且使用枚举类Enum实现了序列化接口,并且也实现了readResolve()方法,因为使用序列化方式也没法破坏,是最理想的单例模式实现。?

热点排行