//创建一个ObjectOutputStream输出流
oos =newObjectOutputStream(
newFileOutputStream("object.txt"));
Person per =newPerson("孙悟空",500);
//将per对象写入输出流
oos.writeObject(per);
如果希望从二进制流中恢复Java对象,则需要使用反序列化,具体步骤如下:
a. 创建ObjectInputStream对象,它需要包含的节点流需要和对应ObjectOutputStream包含的节点流对象对应;
b. 调用readObject()方法,返回一个Object对象,就是当初被序列化的对象。代码如下:
//创建一个ObjectInputStream输出流
ois =newObjectInputStream(
newFileInputStream("object.txt"));
//从输入流中读取一个Java对象,并将其强制类型转换为Person类
Person p = (Person)ois.readObject();

在序列化中,需要注意如下几点:

a. 反序列化读取的仅仅是Java对象的数据,注意是数据,包括对象的类名和属性(不包括方法,静态属性以及transient属性)而不是对象Java类。因此采用反序列化时,必须提供该Java对象所属的class类文件,在反序列化后仍然后可调用序列化后的的对象以及没有被序列化的属性,是由这个class文件保证的;

b. 在序列化机制下读出对象的顺序需要与当时写入的顺序一致;

c. 被序列化的对象类的父类要么是可被序列化的,要么有无参的构造方法;由于在创建子类时,系统会隐式的创建其父类的实例,因此在反序列化时,也需要恢复其父类的实例,也可以调用父类的无参构造方法。

2. 对象引用的序列化

如果某个需要被序列化的对象中有属于另外一个类的属性,那么另外的那个类也必须是可以被序列化的,否则,即使该对象中实现了Serializable接口,也无法将它序列化。在Java中的序列化机制中提供了如下算法:一个对象只能被序列化一次,以后的序列化操作并不产生作用而仅仅是返回第一次序列化对象的编号。重复序列化对象之后再进行反序列化操作得到的是指向同一个对象的引用。即使在该对象序列化之后,修改其属性值后在进行序列化,反序列化得到的还是第一次序列化的对象。

publicclassWriteTeacher
{
publicstaticvoidmain(String[] args)
{
ObjectOutputStream oos =null;
try
{
//创建一个ObjectOutputStream输出流
oos =newObjectOutputStream(
newFileOutputStream("teacher.txt"));
Person per =newPerson("孙悟空",500);
Teacher t1 =newTeacher("唐僧", per);
Teacher t2 =newTeacher("菩提祖师", per);
//依次将四个对象写入输出流
oos.writeObject(t1);
oos.writeObject(t2);
oos.writeObject(per);
oos.writeObject(t2);
}
catch(IOException ex)
{
ex.printStackTrace();
}
finally
{
try
{
if(oos !=null)
oos.close();
}
catch(IOException ex)
{
ex.printStackTrace();
}
}
}

上面代码中写入对象的最后一行代码实际上并没有写入teacher对象,因为在第二行写入代码中已经将其写入。

如果对于需要序列化的对象中的属性不像被序列化,即这个属性的值不会被转化为二进制参数,那么需要在这个属性前面加上transient关键字。例如下面的代码。

publicclassPerson
implementsjava.io.Serializable
{
privateString name;
//这里声明整型变量age为transient,则它将不会被序列化,即它的值不会被序列化成为二进制流,在反序列化中读出的值应为默认值0。
privatetransientintage;
publicPerson(String name ,intage)
{
System.out.println("有参数的构造器");
this.name = name;
this.age = age;
}
publicvoidsetName(String name)
{
this.name = name;
}
publicString getName()
{
returnthis.name;
}
publicvoidsetAge(intage)
{
this.age = age;
}
publicintgetAge()
{
returnthis.age;
}
}

另外对于那些对序列化有特殊要求的类,可以在其中定义自己的序列化机制。在序列化和反序列化时并无异样,但是对象属性是按照我们自定义的机制进行的序列化。代码如下:

publicclassPerson
implementsjava.io.Serializable
{
privateString name;
privateintage;
publicPerson(String name ,intage)
{
System.out.println("有参数的构造器");
this.name = name;
this.age = age;
}
publicvoidsetName(String name)
{
this.name = name;
}
publicString getName()
{
returnthis.name;
}
publicvoidsetAge(intage)
{
this.age = age;
}
publicintgetAge()
{
returnthis.age;
}
//这里定义了对person对象的序列化机制
privatevoidwriteObject(java.io.ObjectOutputStream out)
throwsIOException
{
out.writeObject(newStringBuffer(name).reverse());
out.writeInt(age);
}
//这里定义了对person对象的序列化机制
privatevoidreadObject(java.io.ObjectInputStream in)
throwsIOException, ClassNotFoundException
{
this.name = ((StringBuffer)in.readObject()).reverse().toString();
this.age = in.readInt();
}
}

对于需要实现序列化的对象来说,它的类还可以实现另外一种接口Externalizeable。实现了这个接口的类可以被序列化,上面方法不同的是,必须实现对于序列化机制自定义的writeExternal()和readExternal()方法。

利用被序列化类中的serialVersionUID可以保证即使原来的class被修改以后,仍然可以正常序列化而不产生不兼容的错误,但反序列化后的对象还是修改前的对象,可以像下面这样:

privatestaticfinallongserialVersionUID=512L;