JAVA 中对序列化的支持 都是需要实现 Serializable 接口,然后需要声明一个serialVersionUID (也可以不用申明).
serialVersionUID 的作用是什么呢?
JAVA序列化的机制是通过判断类的serialVersionUID来验证的版本一致的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID于本地相应实体类的serialVersionUID进行比较。如果相同说明是一致的,可以进行反序列化
public class User implements Serializable {
private static final long serialVersionUID = -3350966179389669783L;
private String name;
显示申明是根据类名 字段 参数 生成的一个64位的hash值(申明之后就不会变了 就算修改了类信息)
public class User implements Serializable {
private String name;
隐式申明 也是会根据类名 字段 参数 生成的一个64位的hash值(但是每次类信息修改 都会重新生成)
声明 User 类 (不申明serialVersionUID)持久化到文件, 在User 类中新增字段 在从文件读取
public class User implements Serializable {
private String name;
private int age;
private static final String FILE = "/Users/xxx/Desktop/dto";
@Test
public void serialize() {
User user = new User();
user.setName("sunla");
user.setAge(31);
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(user);
os.close();
bos.close();
FileUtils.writeByteArrayToFile(new File(FILE),bos.toByteArray());
} catch (IOException e) {
throw new IllegalArgumentException("Non-serializable object", e);
}
}
public class User implements Serializable {
private String name;
private int age;
/** 新增属性 */
private HashSet<String> sets = new HashSet<>();
private static final String FILE = "/Users/xxx/Desktop/dto";
@Test
public void deserialize() {
Object rv = null;
try {
byte[] in = FileUtils.readFileToByteArray(new File(FILE));
if (in != null) {
ByteArrayInputStream bis = new ByteArrayInputStream(in);
ObjectInputStream is = new ObjectInputStream(bis);
rv = is.readObject();
is.close();
bis.close();
}
User user = (User)rv;
System.out.println(JSON.toJSONString(user));
} catch (Exception e) {
e.printStackTrace();
}
}
java.io.InvalidClassException: com.test.serial.User; local class incompatible: stream classdesc serialVersionUID = 7212634958119174257, local class serialVersionUID = 5030622290002077224
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:687)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1880)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1746)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2037)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1568)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:428)
执行结果报错了,2个很明显的 serialVersionUID 不一样 导致反序列化失败,就是前面说到的 隐式声明的sid 当新增字段后会变更
声明 User 类 (申明serialVersionUID)持久化到文件, 在User 类中新增字段 在从文件读取
public class User implements Serializable {
private static final long serialVersionUID = -3350966179389669783L;
private String name;
private int age;
private static final String FILE = "/Users/xxx/Desktop/dto";
@Test
public void serialize() {
User user = new User();
user.setName("sunla");
user.setAge(31);
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(bos);
os.writeObject(user);
os.close();
bos.close();
FileUtils.writeByteArrayToFile(new File(FILE),bos.toByteArray());
} catch (IOException e) {
throw new IllegalArgumentException("Non-serializable object", e);
}
}
public class User implements Serializable {
private static final long serialVersionUID = -3350966179389669783L;
private String name;
private int age;
/** 新增属性(这里使用了集合 反序列化出来大家可以猜下 调用getSets的结果) */
private HashSet<String> sets = new HashSet<>();
private static final String FILE = "/Users/xxx/Desktop/dto";
@Test
public void deserialize() {
Object rv = null;
try {
byte[] in = FileUtils.readFileToByteArray(new File(FILE));
if (in != null) {
ByteArrayInputStream bis = new ByteArrayInputStream(in);
ObjectInputStream is = new ObjectInputStream(bis);
rv = is.readObject();
is.close();
bis.close();
}
User user = (User)rv;
System.out.println(JSON.toJSONString(user));
System.out.println(user.getSets() == null ? "set null":"set not null");
} catch (Exception e) {
e.printStackTrace();
}
}
{"age":31,"name":"sunla"}
set null
执行结果 是新增的集合对象是个null ,其他字段完整映射