1.什么是序列化和反序列化
序列化就是将java对象转成字节序列的过程;反序列化就是将字节序列转成java对象的过程。
java中,序列化的目的一种是需要将对象保存到硬盘上,一种是对象需要在网络中传输。
2.序列化和反序列化的方式
序列化和反序列化有很多种方式,如JDK类库中提供的序列化API、常用的json工具类等。本篇博客使用JDK提供的序列化API进行演示。重点说明serialVersionUID的作用。
假设现在有一个Student类,我们要对Student类进行序列化操作
①该类必须实现Serializable接口
public class Student implements Serializable{ private static final long serialVersionUID = -595470438262181967L; private String name ; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } }
②在main方法中执行序列化和反序列化操作
public static void main(String[] args) throws Exception { //序列化 OutputStream os = new FileOutputStream(new File("D://student.txt")); ObjectOutputStream oos = new ObjectOutputStream(os); Student student = new Student(); student.setName("张三"); student.setSex("男"); oos.writeObject(student); //反序列化 InputStream is = new FileInputStream(new File("D://student.txt")); ObjectInputStream ois = new ObjectInputStream(is); Student student1 = new Student(); student1 = (Student) ois.readObject(); System.out.println(student1.getName()); }
输出结果:张三
即student1对象在反序列化时进行了赋值
3.为什么要serialVersionUID
serialVersionUID: 字面意思上是序列化的版本号,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量。
(1)下面进行测试,如果没有serialVersionUID会出现什么?
①去掉Student类中的serialVersionUID属性。执行main方法,结果显示序列化成功!输出张三
②修改Student类,在Student类中添加number字段
public class Student implements Serializable{ private String name ; private String sex; private String number; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } }
不执行序列化方法,只执行反序列化方法,结果出现异常:
Exception in thread "main" java.io.InvalidClassException: com.iot.study.serialize.Student; local class incompatible: stream classdesc serialVersionUID = -595470438262181967, local class serialVersionUID = -4254220179260112271 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:621) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371) at com.iot.study.serialize.Client.main(Client.java:35)
意思是原来序列化的时候(没有指定serialVersionUID)硬盘存的class的 serialVersionUID = -595470438262181967,而当前class的serialVersionUID = -4254220179260112271。 二者不一样,无法反序列化。
原因分析:
serialVersionUID没有指定时,java编译器会自动给这个class进行一个摘要算法,类似于指纹算法,只要这个文件 多一个空格,得到的UID就会截然不同的,可以保证在这么多类中,这个编号是唯一的。所以,添加了一个number字段后,由于没有显指定 serialVersionUID,编译器又为我们生成了一个UID,当然和前面保存在文件中的那个不会一样了,于是就出现了2个序列化版本号不一致的错误。
(2)指定serialVersionUID测试
如果为Student类显示的指定serialVersionUID,那么在序列化和反序列化的时候,即使修改了Student类中的部分内容,也能序列化成功。
4.serialVersionUID的取值
serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。
类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的 serialVersionUID,也有可能相同。为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。
显式地定义serialVersionUID有两种用途:
1、 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
2、 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。