概念
序列化:把对象转换为字节序列的过程称为对象的序列化。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
目的
① 方便进行数据传输。在分布式系统中,需要把对象在网络上传输,所以需要把对象数据转换为二进制形式。
② 节省服务器内存。如果服务器发现某些对象好久没有活动了,那么服务器就会把这些内存中的对象持久化在本地磁盘文件中(Java对象转换为二进制文件);如果服务器发现某些对象需要活动时,先去内存中寻找,找不到再去磁盘文件中反序列化对象数据,将其恢复为Java对象。
实现
① 实现Serializable或者Externalizable接口。Externalizable接口继承自Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serialzable接口的类可以采用默认的序列化方式。
② 对象序列化
(1) 创建一个对象输出流ObjectOutputStream,它可以包装一个其他类型的目标输出流,如文件输出流。
(2) 通过对象输出流的writeObject()方法写对象。
③ 对象反序列化
(1) 创建一个对象输入流ObjectInputStream,它可以包装一个其他类型的源输入流,如文件输入流。
(2) 通过对象输入流的readObject()方法读取对象。
例子
① 建立一个JavaBean用于序列化和反序列化
/**
* 定义一个Person类,实现Serializable接口
* @author N.Y
*
*/
public class Person implements Serializable{
/**
* 序列化ID
*/
private static final long serialVersionUID = -5809782572943999L;
private int age;
private String name;
private transient String sex; //transient关键字可以使sex这个变量不能被序列化,拥有static关键词的变量也不可以被序列化和反序列化
//详情可以参考这篇文章:
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
}
② 利用ObjectOutputStream和ObjectInputStream来实现对象的序列化和反序列化
public class TestObjSerializeAndDeserialize {
public static void main(String[] args) throws IOException, Exception {
SerializePerson(); //序列化Person对象
Person p = DeserializePerson(); //反序列化Person对象
System.out.println(MessageFormat.format("name={0}, age={1}, sex={2}", p.getName(), p.getAge(), p.getSex()));
}
/**
* 序列化Person对象
* @throws FileNotFoundException
* @throws IOException
*/
private static void SerializePerson()throws FileNotFoundException, IOException{
Person person = new Person();
person.setName("Nicole");
person.setAge(19);
person.setSex("男");
//ObjectOutputStream对象输出流,将Person对象存储到person.txt文件中,完成对Person对象的序列化
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("person.txt")));
oo.writeObject(person);
System.out.println("Person对象序列化成功.");
oo.close();
}
private static Person DeserializePerson() throws Exception,IOException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("person.txt")));
Person person = (Person)ois.readObject();
System.out.println("Person对象反序列化成功.");
return person;
}
}
serialVersionUID的作用
固定版本,因为serialVersionUID取值是Java运行时环境根据类的内部细节自动生成的,只要class文件稍有改变其生成的serialVersionUID就截然不同了,而且也有可能换了个编译器它的serialVersionUID也会不同;可以根据版本号来进行反序列化。
为了提高serialVersionUID的独立性和确定性,建议在一个可序列化类中显式定义serialVersionUID,为它赋予明确的值。一是为了确保类的不同版本具有相同的serialVersionUID,二是即使在某些场合中不希望类的不同版本对序列化兼容,那显式定义serialVersionUID可以确保类的不同版本具有不同的serialVersionUID。