概念:
Java 提供了一种对象序列化的机制,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化。也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。
整个过程都是 Java 虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。
实现:
一个类实现了Serializable接口,它就可以被序列化
public class Employee implements java.io.Serializable
如果被写对象的类型是String,或数组,或Enum,或Serializable,那么就可以对该对象进行序列化,否则将抛出NotSerializableException
JDK中的序列化API:
java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到目标输出流中。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以采用默认的序列化方式 。
对象序列化包括如下步骤:
1)创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
2)通过对象输出流的writeObject()方法写对象。
对象反序列化的步骤如下:
1)创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
2)通过对象输入流的readObject()方法读取对象。
特殊情况:
对于一个实体类,不想将所有的属性都进行序列化,有专门的关键字transient:
private transient int name;
对该类序列化时会自动忽略被 transient 修饰的属性,反序列化后name=0。
更多:
1.序列化ID:
凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量
private static final long serialVersionUID = 1L
情境:两个客户端 A 和 B 试图通过网络传递对象数据,A 端将对象序列化为二进制数据再传给 B,B 反序列化得到对象。
问题:反序列化时总是提示不成功。
解决:虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,还有两个类的序列化 ID 是否一致。
序列化 ID 在 Eclipse 下提供了两种生成策略,一个是固定的 1L,一个是随机生成一个不重复的 long 类型数据(实际上是使用 JDK 工具生成)。随机生成的序列化 ID 有什么作用呢,有些时候,通过改变序列化 ID 可以用来限制某些用户的使用。
2.不保存静态变量
public class Test implements Serializable{
public static int staticVar = 5;
}
//序列化,写入路径result.obj
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("result.obj"));
out.writeObject(new Test());
out.close();
//序列化后修改为10
Test.staticVar = 10;
//反序列化,读取
ObjectInputStream oin = new ObjectInputStream(new FileInputStream("result.obj"));
Test t = (Test) oin.readObject();
oin.close();
System.out.println(t.staticVar); //结果是10
序列化保存对象的状态,静态变量属于类的状态,因此序列化并不保存静态变量。