Java中的序列化与反序列化
序列化与反序列化
什么是序列化和反序列化
Java序列化是指把Java对象转换为字节序列的过程,Java反序列化是指把字节序列恢复为Java对象的过程。通过徐序列化和反序列化实现网络传输、本地存储的目的。
1.Serializable实现Java序列化
要实现Java对象的序列化,只要将类实现标识接口——Serializable接口即可,不需要我们重写任何方法就可以实现序列化。
案例:Java实现Serializable接口进行序列化案例。
编写实体类
import java.io.Serializable;
import java.util.List;
public class Student implements Serializable {
/**
* 学号
*/
private String stuNum;
/**
* 姓名
*/
private String stuName;
/**
* 教师姓名:一个学生可以有多个老师
*/
private List<String> teacherList;
//无参数构造方法
public Student() {
}
//全参构造方法
public Student(String stuNum, String stuName, List<String> teacherList) {
this.stuNum = stuNum;
this.stuName = stuName;
this.teacherList = teacherList;
}
@Override
public String toString() {
return "Student{" +
"stuNum='" + stuNum + '\'' +
", stuName='" + stuName + '\'' +
", teacherList=" + teacherList +
'}';
}
public String getStuNum() {
return stuNum;
}
public void setStuNum(String stuNum) {
this.stuNum = stuNum;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public List<String> getTeacherList() {
return teacherList;
}
public void setTeacherList(List<String> teacherList) {
this.teacherList = teacherList;
}
}
编写Java对象序列化和反序列化工具类
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.InputStream;
public class MySerializeUtil {
/**
* 将对象序列化到指定文件中
* @param obj
* @param fileName
*/
public static void mySerialize(Object obj,String fileName) throws
IOException {
OutputStream out=new FileOutputStream(fileName);
ObjectOutputStream objOut=new ObjectOutputStream(out);
objOut.writeObject(obj);
objOut.close();
}
/**
* 从指定文件中反序列化对象
* @param fileName
* @return
1.1.3 测试对象的序列化和反序列化
运行结果:
*/
public static Object myDeserialize(String fileName) throws IOException,
ClassNotFoundException {
InputStream in=new FileInputStream(fileName);
ObjectInputStream objIn=new ObjectInputStream(in);
Object obj=objIn.readObject();
return obj;
}
}
测试对象的序列化和反序列化
public class MainTest {
public static void main(String[] args) {
List<String> teacherList=new ArrayList<>();
teacherList.add("空空道人");
teacherList.add("贾代儒");
Student stu1=new Student("1001", "贾宝玉", teacherList);
System.out.println("原始对象:"+stu1);
String fileName="stu01.txt";
try {
//对象序列化
MySerializeUtil.mySerialize(stu1, fileName);
System.out.println("序列化原始对象完成!OK!");
//对象的反序列化
Object obj=MySerializeUtil.myDeserialize(fileName);
if(obj instanceof Student){
Student stuNew= (Student) obj;
System.out.println("反序列化之后的对象:"+stuNew);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果
部分属性的序列化
实现部分字段序列化的方式:
使用transient修饰符
使用static修饰符
默认方法writeObject和readObject。
使用transient修饰符
修改实体类,将实体类中不想序列化的属性添加transient修饰词。
public class Student implements Externalizable {
private String stuNum;
private transient String stuName;
private transient List<String> teacherList;
......
重新运行测试类的结果:
我们将实体类中的stuName和teacherList属性添加了transient修饰词,因此对象被序列化的时候忽略这两个属性。通过运行结果可以看出。
使用static修饰符
static修饰符修饰的属性也不会参与序列化和反序列化。
修改实体类,将实体类中不想序列化的属性添加static修饰词。
public class Student implements Externalizable {
private String stuNum;
private static String stuName;
private List<String> teacherList;
......
修改测试类,在完成原始对象的序列化之后再对static修饰的变量进行一次赋值操作:
重新运行测试类的结果:
我们将实体类中的stuName属性添加了transient修饰词,因此对象被序列化的时候忽略这个属性。通过运行结果可以看出。
默认方法writeObject和readObject
修改实体类,将transient修饰词去掉,添加两个方法。
public class Student implements Serializable {
private String stuNum;
private String stuName;
private List<String> teacherList;
private void writeObject(ObjectOutputStream objOut) throws IOException {
System.out.println("writeObject-----------");
objOut.writeObject(stuNum);
objOut.writeObject(stuName);
}
private void readObject(ObjectInputStream objIn) throws IOException,
ClassNotFoundException {
System.out.println("readObject-----------");
stuNum= (String) objIn.readObject();
stuName= (String) objIn.readObject();
}
......
重新运行测试类的结果:
我们在添加的方法中只对stuNum和stuName属性做了序列化和反序列化的操作,因此只有这个两个属性可以被序列化和反序列化。
2.Externalizable实现Java序列化
刚刚我们说实现部分属性序列化的方式有多种,最后一种来啦!就是通过实现Eexternalizable接口。Externalizable继承自Serializable,使Externalizable接口需要实现readExternal方法和writeExternal方法来实现序列化和反序列化。
代码实现:
public class Student implements Externalizable {
/**
* 学号
*/
private String stuNum;
/**
* 姓名
*/
private String stuName;
/**
* 教师姓名:一个学生可以有多个老师
*/
private List<String> teacherList;
//无参数构造方法
public Student() {
}
//全参构造方法
public Student(String stuNum, String stuName, List<String> teacherList) {
this.stuNum = stuNum;
this.stuName = stuName;
this.teacherList = teacherList;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(stuNum);
out.writeObject(stuName);
//out.writeObject(teacherList);
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
stuNum= (String) in.readObject();
stuName= (String) in.readObject();
//teacherList= (List<String>) in.readObject();
}
@Override
public String toString() {
return "Student{" +
"stuNum='" + stuNum + '\'' +
", stuName='" + stuName + '\'' +
", teacherList=" + teacherList +
'}';
}
public String getStuNum() {
return stuNum;
}
public void setStuNum(String stuNum) {
this.stuNum = stuNum;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public List<String> getTeacherList() {
return teacherList;
}
public void setTeacherList(List<String> teacherList) {
this.teacherList = teacherList;
}
}
测试结果:
Externalizable接口继承了Serializable接口,所以实现Externalizable接口也能实现序列化和反序列化。
Externalizable接口中定义了writeExternal和readExternal两个抽象方法,这两个方法其实对应Serializable接口的writeObject和readObject方法。可以这样理解:Externalizable接口被设计出来的目的就是为了抽象出writeObject和readObject这两个方法,但是目前这个接口使用的并不多。