前言 |
我们在做web项目的时候,在网络传输中,序列化是绕不过去的重要一环。今天就来总结一下序列化到底能为我们做些什么
概念 |
序列化机制:允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,通过网络将这种二进制流传输到另一个网络节点。其他程序一旦获得了这种二进制流(无论磁盘还是网络),都可以将这种二进制流恢复成原来的Java对象。
如何实现一个序列化的操作 |
1、实现Serializable接口
2、调用ObjectInputStream
3、调用ObjectOutputStream
定义一个person类:
public class Person implements Serializable {
private static final long serialVersionUID = -5183762729469760432L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
public String toString(){
return "Person{"+
"name="+name+
",age="+age+
'}';
}
}
客户端使用ObjectOutSteam序列化对象成为一个二进制文件,使用ObjectInputstream反序列化二进制文件,成为java对象
public class WriteObject {
public static void main(String[] args) {
SerializePerson();
DeSerializePerson();
}
private static void SerializePerson(){
try {
ObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("person"));
Person person=new Person("vincent",26);
oos.writeObject(person);
System.out.println("序列化成功");
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void DeSerializePerson(){
try {
ObjectInputStream ois= new ObjectInputStream(new FileInputStream("person"));
Person per=(Person) ois.readObject();
System.out.println(per);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
问题 |
我们见过很多可序列化的类中都有一一个私有的属性:serialVersionUID ,作用是什么
private static final long serialVersionUID = -5183762729469760432L;
我们做个小实验,先执行序列化一个对象,然后把上边这行代码注释掉,在进行反序列化操作,会报出下边这个错误
java.io.InvalidClassException: com.SerializableDemo.Person; local class incompatible: stream classdesc serialVersionUID = -5183762729469760432, local class serialVersionUID = -2791480941090440504
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:621)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.SerializableDemo.WriteObject.DeSerializePerson(WriteObject.java:26)
at com.SerializableDemo.WriteObject.main(WriteObject.java:8)
serialVersionUID 作用:反序列化Java对象时,必须提供该对象的class文件,随着项目的升级,系统的class文件也会升级,就会出现两个class文件的兼容问题。Java序列化机制可以通过判定serialVersionUID 的值来判定两个class文件是否是一个版本,上边的报错就是由于也就是修改过后的class,和文件流中的class不兼容了,出于安全机制考虑,程序抛出了错误,并且拒绝载入。如果没有为指定的class配置serialVersionUID,那么java编译器会自动给这个class进行一个摘要算法,类似于指纹算法,只要这个文件有任何改动,得到的UID就会截然不同的,可以保证在这么多类中,这个编号是唯一的。
所以说为了在反序列化时确保序列化版本的兼容性,我们最好自己在类中加入private static final long serialVersionUID这个类变量。即使对象被序列化后,他所对应的类修改了,该对象依然能被正确的反序列化。
其他关于序列化的小知识 |
- 序列化不保存静态变量
- 要想父类对象也参与序列化操作,那么必须要让父类也实现Serializable接口
- Transient关键字,主要是控制变量是否能够被序列化。如果没有被序列化的成员变量反序列化后,会被设置成初始值,比如String -> null