您的位置:首页 > 编程语言 > Java开发

代码即财富之我学Java对象序列化与反序列化(2)

2015-06-28 00:00 477 查看
摘要: 这篇文章是关于Java对象序列化与反序列知识的一个总结和概括。

我们在程序创建的Java对象都是存在于JVM内存中的,也就是Java对象的生命周期一定不会长于JVM,所以如何以一种持久化的方式保留用户创建的对象呢,这就需要用到对象序列化的知识,将序列化的对象重新还原称为Java对象,即为反序列化。关于Java对象序列化与反序列化,我们需要理解的一点是,序列化只是将Java对象以一种二进制数据流的方式保存到本地文件或其他介质中,它实际上保存的只是实例化对象的一种状态,对于静态成员,是无法序列化保存的。

Java对象要支持序列化,该类必须要具有可序列化的特性,真正提供实现序列化与反序列化方式的ObjectInputStream和ObjectOutputStream类。Java对象序列化的方式可以分为普通方式和定制方式两种,普通方式,借助Serializable接口完成,不需要用户做过多的干涉和修改;定制方式,用户可以使用transient关键字修饰类中某个属性,使该属性屏蔽序列化特性,使用transient关键字屏蔽之后,被transient修饰的字段序列化后取默认值,而非序列化之前的实例化对象值。如果需要序列化该字段,我们可以将transient关键字去掉,也可以在该类中重写readObject和writeObject方法,并在重写方法中继续序列化屏蔽的字段,从而达到反屏蔽的效果。除了使用transient关键字,重写readObject和writeObject方法实现定制序列化的方式,还有一种与Serializable方式完全不同的支持定制序列化的方式,是指定该类继承自Externalizable接口,但是继承自该接口的类必须明确重写readExternal和writeExternal方法才能实现对象序列化与反序列化,在这两个方法里指定需要序列化和反序列化的属性字段,没有指明的属性将不会被序列化。

下面是提供一个程序示例。

import java.io.*;
public class TestObjSer{
public static void main(String[] args) throws Exception{
String stuFilePath = "./student.out";
String codFilePath = "./coder.out";
File file = new File(codFilePath);
if(!file.exists()){
System.out.println("The serialization file does not exist, which will be created right now!");
file.createNewFile();
}
Student stu = new Student("zhangsan", 17, Gender.MALE, "CSU");
Coder coder = new Coder("lisi", 18, Gender.FEMALE, "JAVA");
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file);

//folowing two stream can not be disordered, I have no idea why is this
ObjectOutputStream oos = new ObjectOutputStream(fos);
ObjectInputStream ois = new ObjectInputStream(fis);
serObj(oos,coder);
deSerObj(ois);
}
public static void serObj(ObjectOutputStream oos,Object obj) throws Exception {
oos.writeObject(obj);
oos.close();
}
public static void deSerObj(ObjectInputStream ois) throws Exception {
Person per = (Person)ois.readObject();
ois.close();
System.out.println(per);
}
}

/**
* the enum type is a special type in Java, which is generated from
* class Enum by default and class Enum implements the interface Seralizable
* so it can be serialized and all member of enum should be capital
*/
enum Gender{
MALE,FEMALE
}
/**
* superclass should implement Serialzable
*/
class Person implements Serializable{
private String name;
private int age;
private Gender gender;
public Person(){
System.out.println("Non-Arg constructor be called!");
}
public Person(String name, int age, Gender gender){
System.out.println("Arg constructor be called!");
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public Gender getGender(){
return this.gender;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setGender(Gender gender){
this.gender = gender;
}
public String toString(){
return "Per(name = " + this.getName() + ", age = "
+ this.getAge() + ", gender = " + this.getGender() +")";
}
}
class Student extends Person{
//use transient to avoid the field school be serialized
private transient String school;
public Student(){
super();
}
public Student(String name, int age, Gender gender, String school){
super(name, age, gender);
this.school = school;
}
public String getSchool() {
return this.school;
}
public void setSchool(String school) {
this.school = school;
}
public String toString(){
return "Stu(name = " + this.getName() + ", age = " + this.getAge() +
", gender = " + this.getGender() + ", school = " + this.getSchool() +")";
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();//call super
oos.writeObject(school);
}
private void readObject(ObjectInputStream ois) throws Exception{
ois.defaultReadObject();//call super
this.school = (String)ois.readObject();
}
}
class Coder extends Person implements Externalizable {
private String language;
public Coder(){
super();
}
public Coder(String name, int age, Gender gender, String language){
super(name, age, gender);
this.language = language;
}
public String getLanguage() {
return this.language;
}
public void setLanguage(String language) {
this.language = language;
}
public String toString(){
return "Cod(name = " + this.getName() + ", age = " + this.getAge() +
", gender = " + this.getGender() + ", language = " + this.getLanguage() +")";
}
/**
* [readExternal description]serialize by this method should
* specify field in readExternal and writeExternal method by user,
* it is completely different from Serialzable
* @param  in                     [description]
* @throws IOException            [description]
* @throws ClassNotFoundException [description]
*/
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.setName((String)in.readObject());
this.setAge(in.readInt());
this.setGender((Gender)in.readObject());
this.language = (String)in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(this.getName());
out.writeInt(this.getAge());
out.writeObject(this.getGender());
out.writeObject(this.language);
}
}

上面的程序示例基本展示了Java对象序列化与反序列化的所有知识,还有一些相关知识,希望在这里啰嗦几句:

1)enum属于Java中的特殊类型,凡是enum类型声明的变量都是默认继承自Enum类,且默认具有序列化特性。

2)Java对象序列化采取级联机制,即如果一个对象引用了其它对象作为其属性,那么在序列化时,引用对象也将被序列化。

3)要想从父类继承的成员也被序列化,则父类必须明确支持序列化特性,即继承Serializable接口。

4)Java序列化的底层原理利用的是反射机制,这个将在后续介绍。

5)从其他博客看到的一点,关于单例模式单例对象的序列化,要想实现反序列化后的对象与单例对象相等,一个解决方法就是替代序列化过程,在该类中添加一个readResolve方法:无论是实现Serializable接口,或是Externalizable接口,当从I/O流中读取对象时,readResolve()方法都会被调用到。实际上就是用readResolve()中返回的对象直接替换在反序列化过程中创建的对象,而被创建的对象则会被垃圾回收掉(这句话是别人的)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: