本篇文章参考:
JAVA 序列化、反序列化以及serialVersionUID_java serialversionuid-CSDN博客
Java 中序列化与反序列化,看这篇就够了!_java的序列化和反序列的定义-CSDN博客
引言:
Java的JDK中提供了对应的API,来实现序列化和反序列化,就是把对象转化为字节序列以及再转化会对象的功能,便于Java对象的持久化储存和在网络中的传输。
一、简单理解序列化与反序列化的原理
序列化(Serialization):就是把一个普通的Java对象(POJO)转化为特定的字节序列,通过序列化,可以将对象存储到文件中。
反序列化(Deserialization):就是把已经序列化为字节序列的Java对象再转化回对象的过程。在反序列化过程中,Java会读取字节序列中的信息重构对象,并将其重新加载到内存中。
计算机中,文件都是存储在硬盘(外存)中,除非你删除它,不然是会一直保存着的;且文件可以用来传输(例如微信,QQ上传输文件);因此,把Java对象序列化成文件可以实现Java对象持久化储存和在网络中的传输。
Java的序列化机制是通过实现java.io.Serializable
接口来实现的。该接口是一个标记接口,没有定义任何方法。只有实现了Serializable
接口的类创建的对象才能被序列化。
注意, 如果对象的某些属性不想被序列化,那么如何避免序列化呢?
1. 用static 修饰, 因为序列化操作只对于堆区 ,而static修饰的属性在全局区
2.用transient 修饰属性,java语法规定,被transient
关键字修饰的成员属性变量不被序列化
二、实现序列化与反序列化的代码过程
1.序列化对象
1.首先创建一个实现了Serializable
接口类,该类的实例都将可以被序列化
2.创建一个ObjectOutputStream
对象,用该对象中的
writeObject()方法将要被序列化的对象写进输出流中
3.释放资源:关闭输出流。
2.反序列化对象
- 创建一个
ObjectInputStream
对象,用于从字节流中读取对象。 - 使用
readObject()
方法从输入流中读取对象。 - 释放资源:关闭输入流。
代码实现:
创建一个实现了Serialization接口的类
package com.yzx.test;
import java.io.Serializable;
/**
* 实现了序列化接口的学生类
*/
public class Student implements Serializable {
private String name;
private char sex;
private int age;
public Student() {
}
public Student(String name,char sex,int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setSex(char sex) {
this.sex = sex;
}
public void setAge(int year) {
this.age = year;
}
public String getName() {
return this.name;
}
public char getSex() {
return this.sex;
}
public int getAge() {
return this.age;
}
}
下面把该学生类的实例序列化,储存Test\src\student.txt文件中,并从该文件中反序列化,在控制台显示结果。代码如下:
package com.yzx.test;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class SerializeStudent {
public static void main(String[] args) {
Student student = new Student("小明",'男',18);
File file = new File("D:\\eclipse-workspace\\TEST\\src\\student.txt");
try {
file.createNewFile();
}
catch(IOException e) {
e.printStackTrace();
}
try {
//对student对象序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(student);
oos.flush();
oos.close();
//把student对象反序列化并用student1接收
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Student student1 = (Student) ois.readObject();
System.out.println("name = " + student1.getName());
System.out.println("sex = " + student1.getSex());
System.out.println("age = " + student1.getAge());
ois.close();
}
catch(ClassNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
控制台输出:
而打开student.txt文本可看到被序列化的jstudent对象:
三、序列化ID问题
一般在序列化时,会对Student类加上一条 serialVersionUID 的属性如:
private static final long serialVersionUID = 1L;
如果不手动加这个 serialVersionUID,序列化过程中也会默认生成一个的,只是我们看不到。例如
private static final long serialVersionUID = -8567374045705746827L;
在序列化和反序列化过程中,这个serialVersionUID就像是这个类的身份证号码一样,具有唯一识别的性质。
案例:
原本我们的Student类只有3个成员变量;
然后我们进行了序列化, 这时候,默认生成对应的 serialVersionUID,绑定的内容就是这个有2个字段属性的student,已经序列化保存到Test\src\student.txt文件里面了。
现在我们在原来的基础上加一个成员变量: private String school;
此时再来进行反序列化的操作时,就会报错,如下:
package com.yzx.test;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class SerializeStudent {
public static void main(String[] args) {
File file = new File("D:\\eclipse-workspace\\TEST\\src\\student.txt");
try {
//把原student对象反序列化,这次用修改后的Student类的实例student2接收
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Student student2 = (Student) ois.readObject();
System.out.println("name = " + student2.getName());
System.out.println("sex = " + student2.getSex());
System.out.println("age = " + student2.getAge());
ois.close();
}
catch(ClassNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
报错的大体意思是:要反序列化的对象的 serialVersionUID与本地接收对象的 serialVersionUID不一致;
java.io.InvalidClassException: com.yzx.test.Student; local class incompatible: stream classdesc serialVersionUID = -1458264931033465272, local class serialVersionUID = -178992672213206791
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1963)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1829)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2120)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1646)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:482)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:440)
at com.yzx.test.SerializeStudent.main(SerializeStudent.java:30)
为避免这样的错误,我们一般手动加上一条 serialVersionUID 属性;
加上serialVersionUID之后的对象,序列化之后,无论之后怎么修改,只要serialVersionUID不变,反序列化就可以正常进行。