开发中会经常遇到Java Serializable 序列化这个接口及serialVersionUID的设置,下面对其重新认识一下
- 什么叫序列化和反序列化
- 为什么序列号和反序列化
- serialVersionUID 这个的值到底是在怎么设置的,有什么用。有的是1L,有的是一长串数字
序列化:把对象转换为字节序列的过程称为对象的序列化。在代码运行的时候,我们可以看到很多的对象,可以是一个,也可以是一类对象的集合,很多的对象数据,这些数据中,有些信息我们想让他持久的保存起来,那么这个序列化,就是把内存里面的这些对象给变成一连串的字节描述的过程,常见的就是变成文件。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
你可以不用自己去赋值,Java会给你赋值,但是,这个就会出现bug,很不安全,所以,还得自己手动的来。那么,我该怎么赋值,eclipse可能会自动给你赋值个一长串数字。这个是没必要的。可以简单的赋值个 1L,这就可以啦。。这样可以确保代码一致时反序列化成功。
下面为序列号测试的部分代码:
声明一个实体类
public class FlyPig implements Serializable {
//private static final long serialVersionUID = 1L;
private static String AGE = "268";//注意类型
private String name;
private String color;
transient private String car;//注意类型
//private String addTip;//注意测试
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getCar() {
return car;
}
public void setCar(String car) {
this.car = car;
}
//public String getAddTip() {
// return addTip;
//}
//public void setAddTip(String addTip) {
// this.addTip = addTip;
//}
@Override
public String toString() {
return "FlyPig{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
", car='" + car + '\'' +
", AGE='" + AGE+ '\'' +
'}';
}
测试类部分代码:
public class SerializableTest {
public static void main(String[] args) throws Exception {
serializeFlyPig();//序列化
FlyPig flyPig = deserializeFlyPig();//反序列化
System.out.println(flyPig.toString());
}
/**
* 序列化
*/
private static void serializeFlyPig() throws Exception {
FlyPig flyPig = new FlyPig();
flyPig.setColor("black");
flyPig.setName("riemann");
flyPig.setCar("audi");
// ObjectOutputStream 对象输出流,将 flyPig 对象存储到E盘的 flyPig.txt 文件中,完成对 flyPig 对象的序列化操作
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("d:/flypig.txt")));
objectOutputStream.writeObject(flyPig);
System.out.println("FlyPig 对象序列化成功!");
objectOutputStream.close();
}
/**
* 反序列化
*/
private static FlyPig deserializeFlyPig() throws Exception {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("d:/flypig.txt")));
FlyPig flyPig = (FlyPig) objectInputStream.readObject();
System.out.println("FlyPig 对象反序列化成功!");
return flyPig;
}
}
测试一:
运行上面代码,看效果。
会在 d:/flyPig.txt 生成个文件。
打印:
FlyPig 对象序列化成功!
FlyPig 对象反序列化成功!
FlyPig{name='riemann', color='black', car='null', AGE='268'}
表明:实现了对象的序列化和反序列化。transient 修饰的属性,是不会被序列化的。我设置的奥迪车不见啦,成了null。
测试二:
public static void main(String[] args) throws Exception {
serializeFlyPig();
// FlyPig flyPig = deserializeFlyPig();
// System.out.println(flyPig.toString());
}
运行上面代码,意思也就是说,先序列化个对象到文件了。这个对象是带静态变量的static。
打印:FlyPig 对象序列化成功!
修改flyPig类里面的AGE的值,给改成26
public static void main(String[] args) throws Exception {
// serializeFlyPig();
FlyPig flyPig = deserializeFlyPig();
System.out.println(flyPig.toString());
}
再次运行上面代码
打印:
FlyPig 对象反序列化成功!
FlyPig{name='riemann', color='black', car='null', AGE='26'}
表明:刚刚序列化的268,没有读出来。而是刚刚修改的26,正确结果,应该是覆盖这个26,是268才对。则这个静态static的属性,他不序列化。
测试三:
去掉实体类实现的这个Serializable接口。然后执行后面的序列化和反序列化的方法。直接报错。
抛异常:NotSerializableException
或
先是单独执行序列化方法。生成文件。然后,打开属性 addTip ,这之后,再次执行反序列化方法,看现象
抛异常:Exception in thread "main" java.io.InvalidClassException: javabase.common.FlyPig; local class incompatible: stream classdesc serialVersionUID = 6087327989343454976, local class serialVersionUID = -8001864882446111057
因为在实体类里面是没有明确的给这个 serialVersionUID 赋值,但是,Java会自动的给我赋值的,这个值跟这个model的属性相关计算出来的。
执行序列化的时候,那时候还没有这个addTip属性呢,所以,自动生成的serialVersionUID 这个值,在我反序列化的时候Java自动生成的这个serialVersionUID值是不同的,他就抛异常啦。
测试四:
再来一次,就是先序列化,这个时候,把 private static final long serialVersionUID = 1L; 这行代码的注释打开。那个addTip属性先注释掉,序列化之后,再把这个属性打开,再反序列化。看看什么情况。
运行代码一切ok。
序列化的时候,是没的那个属性的,在发序列化的时候,对应的实体类多了个属性,但是,反序列化执行OK,没出异常。
意义:
首先,你要是不知道这个序列化是干啥的,万一他真的存储了或传输了,你就给实体类实现了个这个接口,你没写这个 serialVersionUID 那么在后来扩展的时候,可能就会出现不认识旧数据的bug,那不就炸啦吗。回忆一下上面的这个出错情况。想想都可怕,这个锅谁来背?那么就是在实现这个Serializable 接口的时候,一定要给这个 serialVersionUID 赋值,就是这么个问题。
serialVersionUID 的值
首先,你可以不用自己去赋值,Java会给你赋值,但是,这个就会出现上面的bug,很不安全,所以,还得自己手动的来。那么,我该怎么赋值,eclipse可能会自动给你赋值个一长串数字。这个是没必要的。可以简单的赋值个 1L,这就可以啦。。这样可以确保代码一致时反序列化成功。不同的serialVersionUID的值,会影响到反序列化,也就是数据的读取,你写1L,注意L大些。计算机是不区分大小写的,但是,作为观众的我们,是要区分1和L的l,所以说,这个值,闲的没事不要乱动,不然一个版本升级,旧数据就不兼容了,你还不知道问题在哪。。。
序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。
如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException。可序列化类可以通过声明名为 “serialVersionUID” 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID:
强烈建议 所有可序列化类都显式声明 serialVersionUID 值,原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。
本文深入探讨Java中序列化与反序列化的概念及其应用场景,解释了serialVersionUID的作用和重要性,并通过实例演示了如何实现序列化过程。
1万+

被折叠的 条评论
为什么被折叠?



