引入
我们知道在java中一切即对象,那我们如果想存储对象或者传送对象时该怎么办?对象又不是字节或者字符,不能直接用输入输出流来进行读写。这时就用到序列化了。
概念
维基百科上的定义是:
对同步控制而言,表示强制在同一时间内进行单一存取。
在数据储存与传送的部分是指将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等,或者透过网络传送资料时进行编码的过程,可以是字节或是XML等格式。而字节的或XML编码格式可以还原完全相等的对象。这程序被应用在不同应用程序之间传送对象,以及服务器将对象储存到档案或数据库。相反的过程又称为反序列化。
这篇博客主要讲数据储存与传送的定义。我们知道在java中一切即对象,那我们如果想存储对象或者传送对象时该怎么办?对象又不是字节或者字符,不能直接用输入输出流来进行读写。
那么这时我们对对象的操作就应该把对象转换为字节序列,而后采用输入输出流来进行操作。比如存储到硬盘或者在网络中传输,以达到传递对象的目的。
当你从硬盘中取出或者在网络中接收到字节序列后,再转换为对象,即反序列化。
实现
只有实现了Serializable和Externalizable接口的类的对象才能被序列化。
ObjectOutputStream类中的writeObject()方法用来写序列化的对象。ObjectInputStream的readObject()方法读取对象。
import java.io.*;
public class Main {
public static void main(String[] args)throws Exception {
Person person = new Person();
person.setName("小明");
person.setAge(19);
person.setSex("男");
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("/Users/lixingyu/object.txt")));
oo.writeObject(person);
oo.close();
System.out.print("write ok !\n");
ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("/Users/lixingyu/object.txt")));
Person person1 = (Person) oi.readObject();
System.out.print(person1.getName()+"\n");
System.out.print("read ok!\n");
}
static class Person implements Serializable{
private static final long serialVersionUID = -5809782578272943999L;
public String name;
public int age;
public String sex;
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;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
}
serialVersionUID
注意到上面的代码中有个serialVersionUID,而且只有一个初始化,下面的代码并没有用到该变量,那么它是干什么的呢?
如果我们对上面的内部类做个修改,删除该变量,先将对象存入硬盘,再在Person类中增加一个变量friend,读取会有什么结果呢?(这里要运行两次,一次只存,第二次,增加变量后只读。
import java.io.*;
public class Main {
public static void main(String[] args)throws Exception {
Person person = new Person();
person.setName("小明");
person.setAge(19);
person.setSex("男");
// ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("/Users/lixingyu/object.txt")));
// oo.writeObject(person);
// oo.close();
System.out.print("write ok !\n");
ObjectInputStream oi = new ObjectInputStream(new FileInputStream(new File("/Users/lixingyu/object.txt")));
Person person1 = (Person) oi.readObject();
System.out.print(person1.getName()+"\n");
System.out.print("read ok!\n");
}
static class Person implements Serializable{
// private static final long serialVersionUID = -5809782578272943999L;
public String name;
public int age;
public String sex;
public String friends;
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;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
}
你会看到下面的报错信息:
Exception in thread "main" java.io.InvalidClassException: Main$Person; local class incompatible: stream classdesc serialVersionUID = -3488593190474644338, local class serialVersionUID = 1566115588131499685
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
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 Main.main(Main.java:15)
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:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
事实上,如果你没有为类添加serializableUID,编译器会自动添加生产一个ID,而当我们对实现序列化的接口类的源代码文件,做修改,即使是只添加一个空格,编译器也会生成一个不同的ID,这样当读取之前存放到硬盘上的序列化对象时,编译器会报错,拒绝载入。
所以serializableUID是用来区别序列化对象的,必须要添加,否则很容易出错。
用途
比如上面的从硬盘中存取对象;在android开发中利用Intent在Activity之间传递对象时,有一种方法是,要求这个对象必须是序列化的;还有通过网络传递对象。