序列化的应用场景有很多,比如网络传输是以字节流的方式对数据进行传输的,或者是将对象数据在进程之间进行传递,都需要进行序列化和反序列化。
Serializable
Serializable是Java接口,单从源码上看,它是一个空接口。实现了Serializable接口的类可以被ObjectOutputStream转换为字节流,同时也可以通过ObjectInputStream再将其解析为对象。
public interface Serializable {
}
不能被序列化的transient和static
因为序列化实际上是保持一个对象实例的状态,而静态变量是属于类的状态,所以static变量是不能序列化的。
同时,如果有一部分变量,是开发者不希望持久化的,可以声明为transient类型,这一类变量不会被持久化和反序列化。
ArrayList的序列化
实际上,ArrayList内部把elementData数组声明为transient类型,是不会被序列化的。但是它自己实现了writeObject和readObject方法。
因为ArrayList内部的数组有动态扩容机制,数组大小总是大于实际元素的个数,如果把整个elementData数组进行序列化,会导致开销过大,影响效率。
序列化和反序列化的过程
我们可以自己实现一个列子,通过堆栈打印来分析序列化和反序列化的过程。
从堆栈中可以看到,最终ObjectStreamClass是通过反射来调用对象的writeObject方法。
反射的代码如下:
/**
* Returns non-static private method with given signature defined by given
* class, or null if none found. Access checks are disabled on the
* returned method (if any).
*/
private static Method getPrivateMethod(Class<?> cl, String name,
Class<?>[] argTypes,
Class<?> returnType)
{
try {
Method meth = cl.getDeclaredMethod(name, argTypes);
meth.setAccessible(true);
int mods = meth.getModifiers();
return ((meth.getReturnType() == returnType) &&
((mods & Modifier.STATIC) == 0) &&
((mods & Modifier.PRIVATE) != 0)) ? meth : null;
} catch (NoSuchMethodException ex) {
return null;
}
}
Externalizable
另外一种序列化的方式是Externalizable,它也是一个接口,继承了Serializable。
public interface Externalizable extends java.io.Serializable {
void writeExternal(ObjectOutput out) throws IOException;
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}
前面的Serializable方式,我们可以选择是否主动重写writeObject和readObject方法。
而Externalizable方式,必须重写writeExternal和readExternal方法。
从源码可以看出来,这两种序列化方式,前面几步都是一样的,在writeOrdinaryObject或readOrdinaryObject方法中,会根据对象的类型选择不一样的实现方式。
private void writeOrdinaryObject(Object obj,
ObjectStreamClass desc,
boolean unshared)
throws IOException
{
//……
if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {
writeSerialData(obj, desc);
}
//……
}
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
//……
ObjectStreamClass desc = readClassDesc(false);
desc.checkDeserialize();
//……
if (desc.isExternalizable()) {
readExternalData((Externalizable) obj, desc);
} else {
readSerialData(obj, desc);
}
//……
}
上面readOrdinaryObject方法中,会先检查对象是否支持反序列化,Externalizable方式有点特殊,它要求对象的类必须有一个无参构造函数,否则会抛出InvalidClassException异常。
总结一下,Externalizable的具体实现方式由开发者自己控制,Serializable方式可以交给Java实现,同时transient类型在Externalizable方式中没有意义。
參考
https://www.cnblogs.com/aoguren/p/4767309.html
https://www.jianshu.com/p/32a2ec8f35ae