序列化和反序列化
序列化
1.将内存中的Java对象传输或者存储到硬盘文件或其他设备文件上的一个过程。
2.在Java对象传输的过程中有可能是跨网络传输,此时Java对象被拆分成无数个数据后一个个得传输。
3.拆分的过程就是将内存中的Java对象转化为字节流的过程。
4.序列化也称"拆分对象"
反序列化
1.将存储在硬盘文件上的Java对象再重新恢复到内存中的过程。
2.反序列化也称"组装对象"
参与序列化和反序列化的对象,必须实现Serializable接口,但该接口中什么代码也没有,它只是一个标识,标识给JVM看的。JVM看到这个接口之后,会为该类自动生成一个序列化版本号。没有实现该接口时,报错“NotSerializableException”
序列化的实现
Java对象Student类
public class Student implements Serializable {
private int no;
private String name;
省略setter和getter
public Student(int no, String name) {
this.no = no;
this.name = name;
}
}
序列化实现测试类
public class ObjectOutputStreamTest01 {
public static void main(String[] args) throws IOException {
//创建Java对象
Student s = new Student(211,"雷磊");
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("students"));
//序列化对象
oos.writeObject(s);
//刷新
oos.flush();
//关闭
oos.close();
}
}
当Student类没有实现Serializable接口时,报错NotSerializableException
反序列化的实现
public class ObjectInputStreamTest {
public static void main(String[] args) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("students"));
//开始序列化,读我们序列化时创建的‘students’文件
Object object = ois.readObject();
System.out.println(object);
ois.close();
}
}
序列化多个对象
public class ObjectOutputStreamList {
public static void main(String[] args) throws Exception{
//创建多个Java对象
List<User> list = new ArrayList<>();
list.add(new User(18,"张三"));
list.add(new User(16,"狗剩"));
list.add(new User(17,"翠花"));
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users"));
oos.writeObject(list);
oos.flush();
oos.close();
}
}
public class ObjectInputStreamList {
public static void main(String[] args) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("users"));
// Object obj = ois.readObject();
// System.out.println(obj instanceof List);
List<User> list = (List<User>) ois.readObject();
for (User user : list){
System.out.println(user);
}
ois.close();
}
}
public class User implements Serializable {
private int age;
private String name;
public User(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
getter和setter省略
}
一次序列化多个对象时,参与序列化的ArrayList集合以及集合中的对象User都需要实现“java.io.Serializable”
ArrayList源码中已经实现了Serializable接口。
transient关键字
transient关键字表示游离的,不参与序列化
idea生成序列化版本号
序列化版本号的作用
如果不指定版本号,当代码后期有修改,修改之后再重新编译就会由如下报错,提示序列化版本不一致。
Connected to the target VM, address: '127.0.0.1:59813', transport: 'socket'
Exception in thread "main" java.io.InvalidClassException:
com.lei.serialize.pojo.User;
local class incompatible:
stream classdesc serialVersionUID = 3903506952886969445,
local class serialVersionUID = 3407519326099955091
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2028)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1875)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2209)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1692)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:508)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:466)
at java.util.ArrayList.readObject(ArrayList.java:799)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1185)
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2345)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2236)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1692)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:508)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:466)
at com.lei.serialize.ObjectInputStreamList.main(ObjectInputStreamList.java:19)
Disconnected from the target VM, address: '127.0.0.1:59813', transport: 'socket'
Process finished with exit code 1
Java区分类的机制
Java语言中是采用什么机制来区分类的?
第一:首先通过类名进行对比,如果类名不一样,肯定不是同一个类。
第二:如果类名一样,就靠序列化版本号进行区分。
如果两个程序员编写了同一个类名的Java对象Student,这个时候序列化版本就起作用了。
对于Java虚拟机来说,java虚拟机可以通过序列化来区分两个类,这要这俩个类都实现了Serializable接口,序列号版本号不一样所以就区分了。
注意:
这种有Java虚拟机自动生成的序列化版本号有缺陷:一旦代码确定之后,不能进行后续的修改;
因为只有修改,必然会重新编译,此时就会生成新的序列化版本号,
而此时Java虚拟机就会认为这是一个全新的类了,因为序列化版本号不一致了,所以就会报错。
所以建议建议手动指定序列化版本号,写死在Java对象的代码里(自由Idea自动生成)。
private static final long serialVersionUID = 2551410408049453941L;