- 1.概念
- 2.序列化方法
- 为什么要实现Serializable接口?
- 3.反序列化方法
- 4.多个对象的序列化与反序列化
- 5.transient关键字
- 6.关于序列化版本号
序列化(Serialize):将Java对象存储到文件中。将Java对象的状态保存下来的过程。(将对象变为二进制比特流(字节)的过程。)序列化需要使用到的类:ObjectOutputStream(写)
反序列化(DeSerialize):将硬盘上的数据重新恢复到内存当中,恢复成Java对象。(将字节恢复成对象的过程。)反序列化需要使用到的类:ObjectInputStream(读)
2.序列化方法 为什么要实现Serializable接口?需要对被序列化的对象的类实现Serializable接口,否则会出现异常:java.io.NotSerializableException。就是有一个Student类要对Student类的对象stu实现序列化,那么Student类就要实现Serializable接口。
通过源代码发现Serializable接口是一个标志接口,即接口中没有方法和属性(什么代码都没有)。Java虚拟机看到erializable标志会对实现了这个接口的类自动生成一个序列化版本号。序列化版本号是使用Java虚拟机默认提供的,也可自己设置。序列化版本号具体介绍见6
Java中有两种接口:普通接口、标志接口。标志接口起到标识的作用,标志接口是给Java虚拟机参考的。Java虚拟机看到这个标志会对实现了这个接口的类进行一些特殊的操作。
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class SerializeDemo {
public static void main(String[] args) throws Exception{
//创建对象
Student s=new Student(11,"qwe");
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("students") );
//"students"这里面存放的是序列化后的文件的存放发位置
//序列化对象
oos.writeObject(s);
//刷新
oos.flush();
//关闭
oos.close();
}
}
public class Student implements Serializable {//标志性接口
private int id;
private String name;
public Student(){ }
public Student(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + ''' +
'}';
}
}
序列化结果为
3.反序列化方法ObjectInputStream ois=new ObjectInputStream(new FileInputStream("students"));
//反序列化,读
Object object = ois.readObject();
System.out.println(object);
4.多个对象的序列化与反序列化
将对象放到集合中序列化集合
public class SerializeDemo {
public static void main(String[] args) throws Exception{
List list=new ArrayList<>();
//创建对象
Student s=new Student(11,"qwe");
list.add(s);
list.add(new Student(12,"we"));
list.add(new Student(13,"wr"));
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list") );
//序列化集合,该集合中其他的对象
oos.writeObject(list);
//刷新
oos.flush();
//关闭
oos.close();
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("list"));
//反序列化,读
List l=(List)ois.readObject();
for (Student c:l
) {
System.out.println(c);
}
ois.close();
}
}
5.transient关键字
transient是游离的意思,表示修饰的成员变量不参与序列化。
将4当中的Student类的id属性用transient关键字修饰,代码执行的结果为
private transient int id; private String name;
6.关于序列化版本号Student{id=0, name=‘qwe’}
Student{id=0, name=‘we’}
Student{id=0, name=‘wr’}
Java语言中是采用什么机制来区分类的?
首先通过类名进行对比,类名不一样不为同一个类,其次如果类名一样,依靠序列化版本号来区分类(即两个类的类名都为com.serialize.Student,此时就要依靠序列化版本号来区分)。即序列化版本号的作用是来区分类的
代码编写后,过了很长时间要对Student类的源代码进行改动,将改动的代码重新编译后,就生成了新的字节码文件。并且当class文件再次运行时,Java虚拟机生成的序列化版本号也会发生变化。当用同样的代码再次进行反序列化(没有再次进行序列化)时会产生异常(java.io.InvalidClassException: com.serialize.Student; local class incompatible: stream classdesc serialVersionUID = -2426745935832763388, local class serialVersionUID = -366168221349123625),即不能进行反序列化。
建议:凡是一个类实现了Serializable接口(标志接口),建议给该类一个固定不变的序列化版本号(手动写出来,不自动生成)即在实现标志接口的类中添加:private static final long serialVersionUID =此处写自己设置的版本号L;
自动生成的序列化版本号的缺点:代码写好后不能再次进行修改类体。
Idea自动生成序列化版本号:
File–>Setting–>Editor–>Inspections–>搜索Serializable—>选择下面图片中的打勾—>Apply–>ok
选择实现标志接口的类的类名–>Alt+Enter–>选择Add ‘serialVersionUID’ field 即可生成序列化版本号



