Java写一个安全的单例模式_Java与设计模式之单例模式(下) 安全的单例模式

importjava.io.ObjectStreamException;importjava.io.Serializable;/*** 序列化的饿汉式单例,线程安全*/

public class EagerSingletonPlus implementsSerializable {private static final EagerSingletonPlus instance = newEagerSingletonPlus();//私有化构造方法

privateEagerSingletonPlus() {

}public staticEagerSingletonPlus getInstance() {returninstance;

}/*** 看这里,新增*/

public Object readResolve() throwsObjectStreamException {returninstance;

}//序列化,新增

private static final long serialVersionUID = -3006063981632376005L;

}

把serialEnumMethod中的EagerSingleton类替换成EagerSingletonPlus,再次执行,结果变成了false,说明反序列化生成的实例instance3和instance1对应同一个实例。因为在反序列化的时候,JVM 会自动调用 readResolve() 这个方法,我们可以在这个方法中替换掉从流中反序列化回来的对象。关于readResolve()的更详细信息,请参考3.7 The readResolve Method 。下面验证基于枚举创建的单例类是不会被序列化破坏的,代码结构和serialMethod()类似。

private static voidserialEnumMethod() {try{

EnumSingleton instance1=EnumSingleton.INSTANCE;//instance3 将从 instance1 序列化后,反序列化而来

EnumSingleton instance3 = null;

ByteArrayOutputStream bout= null;

ObjectOutputStream out= null;try{

bout= newByteArrayOutputStream();

out= newObjectOutputStream(bout);

out.writeObject(instance1);

ByteArrayInputStream bin= newByteArrayInputStream(bout.toByteArray());

ObjectInputStream in= newObjectInputStream(bin);

instance3=(EnumSingleton) in.readObject();

}catch(Exception e) {

System.out.println(" - EagerSingletonPlus ------------- " +e);

}finally{//close bout&out

}

System.out.println("枚举自带光环,反序列化未破坏单例 : " + (instance1 == instance3)); //true

} catch(Exception e) {

e.printStackTrace();

}

}

由此可见,enum 类自带特殊光环,不用写 readResolve() 方法就可以自动防止反序列化方式对单例的破坏,而前四种写法的单例模式则需要特殊处理。原因是在枚举类型的序列化和反序列化上,Java做了特殊的规定在枚举类型的序列化和反序列化上,Java做了特殊的规定。原文如下:

Enum constants are serialized differently than ordinary serializable or externalizable objects. The serialized form of an enum constant consists solely of its name; field values of the constant are not present in the form. To serialize an enum constant, ObjectOutputStream writes the value returned by the enum constant’s name method. To deserialize an enum constant, ObjectInputStream reads the constant name from the stream; the deserialized constant is then obtained by calling the java.lang.Enum.valueOf method, passing the constant’s enum type along with the received constant name as arguments. Like other serializable or externalizable objects, enum constants can function as the targets of back references appearing subsequently in the serialization stream. The process by which enum constants are serialized cannot be customized: any class-specific writeObject, readObject, readObjectNoData, writeReplace, and readResolve methods defined by enum types are ignored during serialization and deserialization. Similarly, any serialPersistentFields or serialVersionUID field declarations are also ignored–all enum types have a fixed serialVersionUID of 0L. Documenting serializable fields and data for enum types is unnecessary, since there is no variation in the type of data sent.

大概意思就是说,在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。

public static > T valueOf(ClassenumType,String name) {

T result=enumType.enumConstantDirectory().get(name);if (result != null)returnresult;if (name == null)throw new NullPointerException("Name is null");throw newIllegalArgumentException("No enum const " + enumType +"." +name);

}

从代码中可以看到,代码会尝试从调用enumType这个Class对象的enumConstantDirectory()方法返回的map中获取名字为name的枚举对象,如果不存在就会抛出异常。再进一步跟到enumConstantDirectory()方法,就会发现到最后会以反射的方式调用enumType这个类型的values()静态方法,也就是上面我们看到的编译器为我们创建的那个方法,然后用返回结果填充enumType这个Class对象中的enumConstantDirectory属性。所以,JVM对序列化有保证。

Reference

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值