Java要求实现序列化接口的类都必须声明一个serialVersionUID静态属性,如果没有该属性JVM也会自动声明该属性,并为该属性赋值(当类发生改变时会赋予不同的值)。该属性的值是唯一的,用于 标识不同的序列化类。只有类的序列化标识完全相同,Java才会进行反序列化工作,这就是序列化标识的作用。
对于前面提到的情景,假设没有手动声明serialVersionUID,则JVM对发送方跟接收方使用的类中的serialVersionUID赋予不同的值,则反序列化失败。当手动给serialVersionUID赋值时,即使类的字 段发生改变,也能够反序列化成功。
自定义序列化策略
定制序列化策略
Java提供了一套有效的机制,允许在序列化和反序列化时,使用定制的方法进行相应的处理。当传输双方协定好序列化策略后,只需要在需要传输的序列化类中添加一组方法来实现这组策略,在序列化时会自动调用这些规定好的方法进行序列化和反序列化。方法如下:
1)private void writeObject(ObjectOutputSteam out) throws IOException
在方法的内部有重要的代码:out.defaultWriteObject() //将对象数据以默认方式写入到输出流中
2)private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException
同样的,此方法内部也有相似代码:in.defaultReadObject(); //以默认方式从输入流中恢复对象
这两个方法的作用分别是将特定的对象写入到输出流中以及从输入流中恢复特定的对象,通过这两个方法,用户即可实现自定义的序列化。当在实现Serializable接口的类中写了上面两个方法之后,序列化或反序列化该类时则会通过反射来调用这两个方法,从而实现自定义序列化。
限制序列化对象的数量
我们看下面的单例模式:
1 public class Singleton implements Serializable { 2 3 private volatile static Singleton mInstance; 4 private Singleton() { 5 } 6 7 public static Singleton getInstance() { 8 if (mInstance == null) { 9 synchronized (Singleton.class) { 10 if (mInstance == null) { 11 mInstance = new Singleton(); 12 } 13 } 14 } 15 return mInstance; 16 } 17 }
此时通过反序列化获取实例,则单例模式会失效。那该如何解决这个问题呢?
Java有一种机制,可以让我们在序列化和反序列化时,可以根据自己的需要,写入或读取指定的实例。使用这种机制,需要在实现Serializable接口的类中添加两个方法:
private Object readResolve() //如果用户在序列化类中添加了该方法,则在进行反序列化时,使用该方法返回的对象,作为反序列化对象。
private Object writeReplace() //如果用户在序列化类中添加了该方法,则在进行序列化时,序列化该类返回的对象。
再看使用了该机制的单例模式:
1 1 public class Singleton implements Serializable { 2 2 3 3 private volatile static Singleton mInstance; 4 4 private Singleton() { 5 5 } 6 6 7 7 public static Singleton getInstance() { 8 8 if (mInstance == null) { 9 9 synchronized (Singleton.class) { 10 10 if (mInstance == null) { 11 11 mInstance = new Singleton(); 12 12 } 13 13 } 14 14 } 15 15 return mInstance; 16 16 } 17 17 18 18 private Object readResolve() { 19 19 return getInstance(); 20 20 } 21 21 22 22 private Object writeReplace() { 23 23 return getInstance(); 24 24 } 25 25 }
此时的通过反序列化得到的对象也是同一个,即单例模式依然有效!