在创建一个实现了序列化接口的class的时候,都会提示你创建一个serialVersionUID (如果你不显示的创建,则jvm会自动创建一个,但是这个id会根据不同的jvm创建不同的id,不推荐)这个到底有什么作用呢?
作用主要用在反序列化的时候,java在做反序列化的时候,将相同serialVersionUID 的类认定为同一个类,这个serialVersionUID 有两种生成方式:
1,采用默认的:比如:`private static final long serialVersionUID = 1L; (这个暂时不太理解,就暂认定为默认值吧)
2,采用生成的:结合类名,方法名,属性及类型产生的一个哈希代码;
有的公司的项目,就是从原来的项目里copy过去,所以`serialVersionUID 也没改,这样的话,当你修改了属性,或者方法后,当java在反序列化的时候,就会认定你和之前的类是同一个对象。这样的话,在本地运行是好的,但是放到tomcat上就抛出javax.servlet.ServletException: BeanUtils.populate 错误。
类A:
public class A implements Serializable{
private static final long serialVersionUID = 1L;
String name;
//set get 方法省略
}
WriteObject
pubic class Test{
A a = new A();
a.setName("a1");
FileOutputStream out = new FileOutputStream(new File("D://1.txt"));
ObjectOutput b = new ObjectOutputStream(out);
b.writeObject(t1);
b.flush();
b.close();
}
ReadObject
public class Test2{
ObjectInput in = new ObjectInputStream(new FileInputStream(new File("D://1.txt")));
A a = (A) in.readObject();
in.close();
System.out.println(a.getName());
}
举个例子:创建一个A类,假设serialVersionUID 为1L,然后创建一个实例a,你把这个类序列化写到本地磁盘的1.txt文件中,然后,你在从1.txt中读取A,也就是对A进行反序列化,如果有以下的操作,则会抛出不一样的错误:
1,没有serialVersionUID ,但是做了一些不兼容的操作(具体哪些是属于不兼容的操作,见文章末尾),比如最简单的,删除了name属性,然后你再运行readObject的操作,就会报错,incompatible types for field name
;
2,修改serialVersionUID :比如修改为2L,运行readObject操作,会抛出:local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
;
附件,不兼容操作:
- 删除属性 - 如果一个类中的某个属性被删除, 那么流的写操作就不会保留那个属性. 当用以前的类来进行流的读取操作的话, 由于流里没有那个属性,这个值就会被设为一个默认值(比如null等)。
- 移动类的继承等级 - 由于数据会出现在不同的序列中,所以这种情况也是不允许的。
- 将nonstatic 属性 更改为 static,或者将 nontransient 属性更改为 transient - 当使用默认的序列化时, 这个操作等同于删除一个属性。
- 更改属性的声明类型 - 每个版本的类会根据他声明的类型来读写属性,所以当声明类型不一致的时候,会报错.
- 重写writeObject 或者 readObject 方法
- 将序列化的类修改为非序列化类