这是最简单的序列化方式。下面的例子将类的内容写到文件中,然后再反序列化声称新的对象。
public class MyClass implements Serializable
{
private String name;
private int value;
public MyClass(String name,int value)
{
this.name=name;
this.value=value;
}
public static void main(String[] args) throws ClassNotFoundException,IOException
{
MyClass c=new MyClass("freebird",8);
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("myclass.out"));
out.writeObject(c);
ObjectInputStream in=new ObjectInputStream(new FileInputStream("myclass.out"));
MyClass c2=(MyClass)in.readObject();
}
}
Serializable接口实际上只是一个标记接口,Java虚拟机利用Class对象帮助实现对象的序列化和反序列化功能。
上面的例子我们使用了文件流,如果使用网络流对象,我们可以轻松的将对象在网络上传递。
如果MyClass对象内部包含了复杂的树结构的成员,这种机制也可以工作的很好。
作为Serializable自动序列化的补充,我们可以将成员变量使用transient关键字修饰。这些变量将不会被序列化和反序列化。
1)序列化格式将成为类的公有接口的一部分,如果类的设计发生改变,则通常序列化的格式也会发生改变,这样就等于该动了类的接口。因此有可能造成类的新旧版本的不兼容。
2)Serializable接口的发序列化机制绕过了默认构造函数。通常我们使用构造函数是为了在对象创建时检查是否符合某些约束条件。Serializable接口的自动反序列化机制不会作到这点。
3)性能不高
上面实现Serializable接口的方式会将所有的成员都序列化,但有时候并不需要将所有成员都序列化成字节流。我们可以让自己的类实现Externalizable接口。Externalizable接口扩展了Serializable接口,并且声明了下面两个抽象方法:
void writeExternal(ObjectOutput out) throws IOException;
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
我们需要实现这两个方法,并且还需要为我们的类提供公有默认构造函数。
public class MyClass implements Externalizable
{
private String name;
private int value;
public MyClass()
{
name="hello";
value=5;
}
public MyClass(String name,int value)
{
this.name=name;
this.value=value;
}
public void writeExternal(ObjectOutput out) throws IOException
{
out.writeObject(name);
out.writeInt(value);
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
name=(String)in.readObject();
value=in.readInt();
}
public static void main(String[] args) throws ClassNotFoundException,IOException
{
MyClass c=new MyClass("freebird",8);
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("myclass.out"));
out.writeObject(c);
ObjectInputStream in=new ObjectInputStream(new FileInputStream("myclass.out"));
MyClass c2=(MyClass)in.readObject();
}
}
这种方法比Java自动序列化机制性能更高。因为程序员只关心某些数据域的序列化,而Java自动化序列化机制必须了解类的全部结构。
这是一种容易引起混乱的做法,并不推荐使用。这种做法就是只实现Serializable接口,不实现Externalizable接口,但是类自己必须提供下面的两个方法,访问权限为私有。
void writeObject(ObjectOutputStream out) throws IOException;
void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
我们并不需要显示的调用这两个方法,Java保证由ObjectOutputStream.writeObject调用我们的writeObject,保证由ObjectInputStream.readObject调用我们的readObject方法。居然可以调用我们的私有方法,这种打破常规的做法当然不值得推荐。
因为Java为每个对象编了一个序号,序号和对象的内存位置对应。第一次反序列化的时候,会创建新对象,第二次就会根据序号进行查找,如果找到该对象,就不再创建,直接返回该对象的引用。
因为有了序号,所以也不会多次重复序列化,特别是写到文件中时。
字符串对象的格式:74 2字节长度 字符串
字符串中的字符都是Unicode字符,采用modified UTF-8格式
类的描述信息格式:
72
2字节长度
类名
8字节指纹 //根据类,父类,接口,域类型和方法签名计算出来的唯一数据
1字节标志
2字节的数据域描述符计数
数据域描述符//1字节类型码+2字节域名长度+域名+类名(如果域为对象)
78(结束标记)
超类(如果没有则为70)
对象格式:73+类描述信息+对象数据
详细请参考<<Core Java 2>> Volum I 12.5.2
关键是知道指纹的存在