目录
1、序列化Serialize和反序列化的概念
在内存和硬盘的数据交互过程中,将Java对象拆分的过程
序列化就是将Java内存中的Java对象切割成像序列一样传递到硬盘文件中存储 (拆分对象)
反序列化就是通过硬盘序列好的Java对象组装重新返回到Java内存中(组装对象)
图示:
2、序列化和反序列化的代码演示:
序列化:
通过代码演示来加深理解:
首先创建一个普通的Student学生类,这个学生类没有实现任何接口,就是普通的:
package com.lbj.javase.bean; import java.io.Serializable; public class Student{ private int no; private String name; public Student() { } public Student(int no, String name) { this.no = no; this.name = name; } public int getNo() { return no; } public void setNo(int no) { this.no = no; } public String getName() { return name; } public void setName(String name) { this.name = name; } //重写toString方法 @Override public String toString() { return "Student{" + "no=" + no + ", name='" + name + '\'' + '}'; } }
再创建一个测试类,将Student对象进行序列化:
package com.lbj.javase.bean; import java.io.FileOutputStream; import java.io.ObjectOutputStream; public class ObjectOutputStreamTest01{ public static void main(String[] args) throws Exception{ //创建java对象 Student s=new Student(123,"zhangsan"); //包装类 FileOutputStream fos=new FileOutputStream("student"); //序列化 ObjectOutputStream oos=new ObjectOutputStream(fos); //序列化对象 oos.writeObject(s); //刷新 oos.flush(); //关闭 oos.close(); } }
报错:
总结:
Student是一个普通的类,并不支持序列化,需要加上Serializable的实现结构
修改测试(此时的Student类实现了Serializable接口):
package com.lbj.javase.bean; import java.io.Serializable; //Serializable只是一个标志性接口,这个接口其实什么都不写 public class Student implements Serializable { private int no; private String name; public Student() { } public Student(int no, String name) { this.no = no; this.name = name; } public int getNo() { return no; } public void setNo(int no) { this.no = no; } public String getName() { return name; } public void setName(String name) { this.name = name; } //重写toString方法 @Override public String toString() { return "Student{" + "no=" + no + ", name='" + name + '\'' + '}'; } }
测试:
package com.lbj.javase.bean; import java.io.FileOutputStream; import java.io.ObjectOutputStream; public class ObjectOutputStreamTest01{ public static void main(String[] args) throws Exception{ //创建java对象 Student s=new Student(123,"zhangsan"); //包装类 FileOutputStream fos=new FileOutputStream("student"); //序列化 ObjectOutputStream oos=new ObjectOutputStream(fos); //序列化对象 oos.writeObject(s); //刷新 oos.flush(); //关闭 oos.close(); } }
测试结果:
总结:
1、参与序列化和反序列化的对象,必须实现Serializable接口
2、Serializable接口只是一个标志性接口,这个接口中什么也没有,只是起到让java虚拟机看到这个类实现了这个接口,可能对这个类特殊的待遇,java虚拟机会默认提供这个序列化的版本号(序列化版本号是用来区分序列化的类是否发生改变,用来达到安全性)
3、java虚拟机会自动序列化版本号,缺点。一旦被序列化的类A的代码确定后,不能进行后续的修改;只要修改,必定会重新编译,此时类A会生成全新的序列化版本号,这时候java虚拟机会认为这是一个全新的类(这样就不好了,会反序列化不成功,因为反序列化是根据序列化版本号进行重新读取)
4、结论:凡是一个类实现了Serializable接口,建议给该类一个固定不变的序列化版本号,这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类(Idea中可以生成一个序列化版本号,自己去探索,或者自己写一个也行)(P756)
-----------------------------------------------------------------------------------------------------------------------------------
反序列化:
代码演示:
package com.lbj.javase.bean;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class ObjectInputStreamTest01 {
public static void main(String[] args) throws Exception{
//包装类
FileInputStream fis=new FileInputStream("student");
//序列化
ObjectInputStream ois=new ObjectInputStream(fis);
//开始反序列化,读
Object obj=ois.readObject();
//输出obj内容
//反序列化回来是一个学生对象,所以会调用学生对象的toString方法
System.out.println(obj.toString());
//关闭流
ois.close();
}
}
输出结果:
Student{no=123, name='zhangsan'}
3、序列化多个对象(序列化集合)
代码演示:
创建一个实现Serializeable接口的Student类:
package com.lbj.javase.bean;
import java.io.Serializable;
//Serializable只是一个标志性接口,这个接口其实什么都不写
public class Student implements Serializable {
private int no;
private String name;
public Student() {
}
public Student(int no, String name) {
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//重写toString方法
@Override
public String toString() {
return "Student{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
序列化:
package com.lbj.javase.bean;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class ObjectOutputStreamTest02 {
public static void main(String[] args) throws Exception{
//创建一个集合对象
List<Student> list=new ArrayList<>();
//添加集合数据
list.add(new Student(111,"lisi"));
list.add(new Student(222,"Wanwu"));
list.add(new Student(333,"liliu"));
//输出流
FileOutputStream fos=new FileOutputStream("students");
//序列化
ObjectOutputStream oos=new ObjectOutputStream(fos);
//序列化一个集合,这个杰辉对象中放了很多其他对象
oos.writeObject(list);
//刷新流
oos.flush();
//关闭流
oos.close();
}
}
反序列化:
package com.lbj.javase.bean;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;
public class ObjectInputStreamTest02 {
public static void main(String[] args) throws Exception{
//输入流
FileInputStream fis=new FileInputStream("students");
//反序列化
ObjectInputStream ois=new ObjectInputStream(fis);
// Object obj=ois.readObject();
// System.out.println(obj.toString());
//用列表形式输出
List<Student> list = (List<Student>) ois.readObject();
//集合的循环输出
for (Student s:list
) {
System.out.println(s);
}
//关闭流
ois.close();
}
}
结果:
Student{no=111, name='lisi'}
Student{no=222, name='Wanwu'}
Student{no=333, name='liliu'}
注意:如果不用集合,直接存储多个对象,在存储第二个对象的时候会报错
4、transient关键字将部分属性不参与序列化
transient表示游离的,不参与序列化,只要给属性加上此限制,即可
演示:
结果(数据变成null):