每当Class implements Serializable时候,我们的目的非常明确:我要持久化.
持久化其实可以这么理解:我们可以通过某种方式将对象从内存块转换为可以传输或可以保存的数据流。等我们想用的时候,再用某种方式将数据流转换为内存块。
在Java中,内存块和数据流的转换我们可以使用“序列化”和“反序列化”,但是当序列化和反序列化的类不一致的情况下,JVM会抛一个InvalidClassException的异常。
比如序列化时候的对象:
反序列化时候的对象:
很明显,序列化时候和反序列化时候两个类的版本明显不同了。JVM检测出了这个版本的差异,并抛出异常。那么JVM是如何判断类的版本呢?JVM是通过流标识符(Stream Unique Identifier)来进行版本的判断的。这个流标识符分为了显式声明和隐式声明两种。
显式声明格式如下:
显式声明其实也就是自己进行声明。那么隐式声明就是自己不声明,在编译的时候,编译器帮我生成。这个隐式声明的规则比较复杂,它会根据包名、类名、继承关系、非私有方法、属性以及参数、返回值等等计算得出的。
JVM在反序列化时,会比较数据流中的serialVersionUID与类的serialVersionUID是否相同,如果不同的话JVM就会抛出InvalidClassException。
显示声明可以让我们“骗过”JVM,使它认为两个版本是完全相同的,这样也是实现了反序列化向上兼容。
public class Person implements Serializable {
}
持久化其实可以这么理解:我们可以通过某种方式将对象从内存块转换为可以传输或可以保存的数据流。等我们想用的时候,再用某种方式将数据流转换为内存块。
在Java中,内存块和数据流的转换我们可以使用“序列化”和“反序列化”,但是当序列化和反序列化的类不一致的情况下,JVM会抛一个InvalidClassException的异常。
比如序列化时候的对象:
public class Person implements Serializable {
private String name = "";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
反序列化时候的对象:
public class Person implements Serializable {
private String sex = "";
private String name = "";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
很明显,序列化时候和反序列化时候两个类的版本明显不同了。JVM检测出了这个版本的差异,并抛出异常。那么JVM是如何判断类的版本呢?JVM是通过流标识符(Stream Unique Identifier)来进行版本的判断的。这个流标识符分为了显式声明和隐式声明两种。
显式声明格式如下:
private static final long serialVersionUID = 8383901821872620925L;
显式声明其实也就是自己进行声明。那么隐式声明就是自己不声明,在编译的时候,编译器帮我生成。这个隐式声明的规则比较复杂,它会根据包名、类名、继承关系、非私有方法、属性以及参数、返回值等等计算得出的。
JVM在反序列化时,会比较数据流中的serialVersionUID与类的serialVersionUID是否相同,如果不同的话JVM就会抛出InvalidClassException。
显示声明可以让我们“骗过”JVM,使它认为两个版本是完全相同的,这样也是实现了反序列化向上兼容。