当两个进程在进行远程通信时彼此可以发送各种类型的数据无论是何种类型的数据都会以二进制序列的形式在网络上传送发送方需要把这个Java对象转换为字节序列才能在网络上传送接收方则需要把字节序列再恢复为Java对象
把Java对象转换为字节序列的过程称为对象的序列化
把字节序列恢复为Java对象的过程称为对象的反序列化
对象的序列化主要有两种用途
) 把对象的字节序列永久地保存到硬盘上通常存放在一个文件中
) 在网络上传送对象的字节序列
一JDK类库中的序列化API
javaioObjectOutputStream代表对象输出流它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化把得到的字节序列写到一个目标输出流中
javaioObjectInputStream代表对象输入流它的readObject()方法从一个源输入流中读取字节序列再把它们反序列化为一个对象并将其返回
只有实现了Serializable和Externalizable接口的类的对象才能被序列化Externalizable接口继承自Serializable接口实现Externalizable接口的类完全由自身来控制序列化的行为而仅实现Serializable接口的类可以采用默认的序列化方式
对象序列化包括如下步骤
) 创建一个对象输出流它可以包装一个其他类型的目标输出流如文件输出流
) 通过对象输出流的writeObject()方法写对象
对象反序列化的步骤如下
) 创建一个对象输入流它可以包装一个其他类型的源输入流如文件输入流
) 通过对象输入流的readObject()方法读取对象
下面让我们来看一个对应的例子类的内容如下
import javaio*
import javautilDate
/**
* 对象的序列化和反序列化测试类
* @author < a >xie>AmigoXie< /a>
* @version
* Creation date 下午
*/
public class ObjectSaver {
/**
* @param args
* @author < a >xie>AmigoXie< /a>
* Creation date 下午
*/
public static void main(String[] args) throws Exception {
ObjectOutputStream out = new ObjectOutputStream
(new FileOutputStream(DobjectFileobj))
//序列化对象
Customer customer = new Customer(阿蜜果 )
outwriteObject(你好!)
outwriteObject(new Date())
outwriteObject(customer)
outwriteInt() //写入基本类型数据
outclose()
//反序列化对象
ObjectInputStream in = new ObjectInputStream
(new FileInputStream(DobjectFileobj))
Systemoutprintln(obj= + (String) inreadObject())
Systemoutprintln(obj= + (Date) inreadObject())
Customer obj = (Customer) inreadObject()
Systemoutprintln(obj= + obj)
int obj = inreadInt()
Systemoutprintln(obj= + obj)
inclose()
}
}
class Customer implements Serializable {
private String name
private int age
public Customer(String name int age) {
thisname = name
thisage = age
}
public String toString() {
return name= + name + age= + age
}
}
输出结果如下
obj=你好!
obj=Sat Sep CST
obj=name=阿蜜果 age=
obj=
因此例比较简单在此不再详述
二实现Serializable接口
ObjectOutputStream只能对Serializable接口的类的对象进行序列化默认情况下ObjectOutputStream按照默认方式序列化这种序列化方式仅仅对对象的非transient的实例变量进行序列化而不会序列化对象的transient的实例变量也不会序列化静态变量
当ObjectOutputStream按照默认方式反序列化时具有如下特点
) 如果在内存中对象所属的类还没有被加载那么会先加载并初始化这个类如果在classpath中不存在相应的类文件那么会抛出ClassNotFoundException
) 在反序列化时不会调用类的任何构造方法
如果用户希望控制类的序列化方式可以在可序列化类中提供以下形式的writeObject()和readObject()方法
private void writeObject(javaioObjectOutputStream out) throws IOException
private void readObject(javaioObjectInputStream in) throws IOException ClassNotFoundException
当ObjectOutputStream对一个Customer对象进行序列化时如果该对象具有writeObject()方法那么就会执行这一方法否则就按默认方式序列化在该对象的writeObjectt()方法中可以先调用ObjectOutputStream的defaultWriteObject()方法使得对象输出流先执行默认的序列化操作同理可得出反序列化的情况不过这次是defaultReadObject()方法
有些对象中包含一些敏感信息这些信息不宜对外公开如果按照默认方式对它们序列化那么它们的序列化数据在网络上传输时可能会被不法份子窃取对于这类信息可以对它们进行加密后再序列化在反序列化时则需要解密再恢复为原来的信息
默认的序列化方式会序列化整个对象图这需要递归遍历对象图如果对象图很复杂递归遍历操作需要消耗很多的空间和时间它的内部数据结构为双向列表
在应用时如果对某些成员变量都改为transient类型将节省空间和时间提高序列化的性能
三 实现Externalizable接口
Externalizable接口继承自Serializable接口如果一个类实现了Externalizable接口那么将完全由这个类控制自身的序列化行为Externalizable接口声明了两个方法
public void writeExternal(ObjectOutput out) throws IOException
public void readExternal(ObjectInput in) throws IOException ClassNotFoundException
前者负责序列化操作后者负责反序列化操作
在对实现了Externalizable接口的类的对象进行反序列化时会先调用类的不带参数的构造方法这是有别于默认反序列方式的如果把类的不带参数的构造方法删除或者把该构造方法的访问权限设置为private默认或protected级别会抛出javaioInvalidException no valid constructor异常
四可序列化类的不同版本的序列化兼容性
凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量
private static final long serialVersionUID
以上serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的如果对类的源代码作了修改再重新编译新生成的类文件的serialVersionUID的取值有可能也会发生变化
类的serialVersionUID的默认值完全依赖于Java编译器的实现对于同一个类用不同的Java编译器编译有可能会导致不同的serialVersionUID也有可能相同为了提高哦啊serialVersionUID的独立性和确定性强烈建议在一个可序列化类中显示的定义serialVersionUID为它赋予明确的值显式地定义serialVersionUID有两种用途
) 在某些场合希望类的不同版本对序列化兼容因此需要确保类的不同版本具有相同的serialVersionUID
) 在某些场合不希望类的不同版本对序列化兼容因此需要确保类的不同版本具有不同的serialVersionUID