概念
- 序列化就是将对象转化为字节流。
- 反序列化就是将字节流转化为对象。
- 默认的序列化是深度系列化(即类中嵌套其他对象引用的对象也会被序列化)。
- 静态成员不会被默认序列化,要让一个类支持序列化只要让这个类实现接口 java.io.Serializable 即可
package java.io;
public interface Serializable {
}
以上是 Serializable
的接口定义,且 Serializable
只是一个没有定义任何方法的标记接口。
为什么定义标记接口即可实现序列化了呢?
声明实现 Serializable
接口后保存读取对象就可以使用 ObjectOutputStream
、ObjectInputStream
流了,ObjectOutputStream
是 OutputStream
的子类,但实现了 ObjectOutput
接口,ObjectOutput
是 DataOutput
的子接口,增加了一个 writeObject(Object obj)
方法将对象转化为字节写到流中,ObjectInputStream
是 InputStream
的子类,实现了ObjectInput
接口,ObjectInput
是 DataInput
的子接口,增加了一个 readObject()
方法从流中读取字节转为对象。
序列化和反序列化的实质在于
ObjectOutputStream
的writeObject
和ObjectInputStream
的readObject
方法实现,常见的String
、Date
、Double
、ArrayList
、LinkedList
、HashMap
、TreeMap
等都默认实现了Serializable
,.
有时候我们对象有些字段的值可能与内存位置(hashcode
)、当前时间等有关,所以我们不想序列化他(因为反序列化后的值是没有意义的),或者有时候如果类中的字段表示的是类的实现细节而非逻辑信息则默认序列化也是不适合的,所以我们需要定制序列化,Java 提供的定制主要有transient
关键字方式和实现 writeObject
、readObject
方式及 Externalizable
接口 readResolve
、writeReplace
方式,还可以将字段声明为 transient
后通过 writeObject
、readObject
方法来自己保存该字段。
默认情况下 Java 会根据类中一系列信息自动生成一个版本号,在反序列化时如果类的定义发生了变化版本号就会变化,也就与反序列化流中的版本号不匹配导致会抛出异常,所以我们为了更好的控制和性能问题会自定义 serialVersionUID
版本号来避免类定义发生变化后反序列化版本号不匹配异常问题,如果版本号一样时流中有该字段而类定义中没有则该字段会被忽略,如果类定义中有而流中没有则该字段会被设为默认值,如果对于同名的字段类型变了则会抛出 InvalidClassException
。
虚拟机是否允许反序列化不仅取决于类路径和功能代码是否一致,还取决于另一个非常重要的点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L
)。
因为声明实现 Serializable
接口后保存读取对象就可以使用 ObjectOutputStream
、ObjectInputStream
流了,ObjectOutputStream
的 writeObject(Object obj)
方法将对象转化为字节写到流中,ObjectInputStream
的 readObject()
方法从流中读取字节转为对象,Serializable
虽然是一个空接口,但是在调用 writeObject
方法时却充当了一种健全的校验作用,如果对象没有实现 Serializable
则在调用 writeObject
时就会抛出异常,所以说 Serializable
算是一种接口标识机制。
如下为 ObjectOutputStream
中 writeObject(Object obj)
的核心标记判断:
private void writeObject0 (Object obj, boolean unshared) throws IOException {
try {
if(obj instanceof Class) {
writeClass((Class) obj, unshared);
} else if(obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
} else if(obj instanceof String) {
writeString((String) obj, unshared);
} else if(cl.isArray()) {
writeArray(obj, desc, unshared);
} else if(obj instanceof Enum) {
writeEnum((Enum) obj, desc, unshared);
} else if(obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if(extendedDebugInfo) {
throw new NotSerializableException(cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
} finally {
}
}