Serializable的过程分析
程序员文章站
2022-05-14 17:38:03
...
本次讲解中我们在上次的基础上,深入的了解一下序列化的流程以及其中的原理。关于序列化的一些知识与使用,请参见我的另一篇博客:java基础---->Serializable的使用。好了,我们进行以下分析的讲解。
目录导航
Java序列化的原理分析
自定义Serializable的使用
友情链接
Java序列化的原理分析
java基础---->Serializable的使用的代码的演示,我们可以知道:
调用writeObject方法序列化一个对象,是将其写入磁盘,以后在程序再次调用readObject时,根据wirteObject方法磁盘的文件重新恢复那个对象
Externalizable 接口扩展了Serializable,并增添了两个方法:writeExternal()和readExternal()。在序列化和重新装配的过程中,会自动调用这两个方法
Java的序列化
一、 objectOutputStream.writeObject(user)方法执行的详细如下:
ObjectOutputStream的构造函数设置enableOverride = false;
public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass();
bout = new BlockDataOutputStream(out);
handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00);
enableOverride = false;
writeStreamHeader();
bout.setBlockDataMode(true);
if (extendedDebugInfo) {
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
所以writeObject方法执行的是writeObject0(obj, false);
public final void writeObject(Object obj) throws IOException {
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {
writeObject0(obj, false);
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throw ex;
}
}
二、 在writeObject0方法中,以下贴出重要的代码
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());
}
}
从上可以看出,如果对象没有实现Serializable接口,在序列化的时候会抛出NotSerializableException异常
上述Person是一个类对象,所以会执行writeOrdinaryObject(obj, desc, unshared)方法;
三、 在writeOrdinaryObject(obj, desc, unshared)方法中,重要代码如下:
if (desc.isExternalizable() && !desc.isProxy()) {
writeExternalData((Externalizable) obj);
} else {
writeSerialData(obj, desc);
}
如果对象实现了Externalizable接口,那么执行writeExternalData((Externalizable) obj)方法
如果对象实现的是Serializable接口,那么执行的是writeSerialData(obj, desc);
四, 我们首先看一下writeSerialData方法,主要执行方法:defaultWriteFields(obj, slotDesc);
private void defaultWriteFields(Object obj, ObjectStreamClass desc) throws IOException {
Class<?> cl = desc.forClass();
if (cl != null && obj != null && !cl.isInstance(obj)) {
throw new ClassCastException();
}
desc.checkDefaultSerialize();
int primDataSize = desc.getPrimDataSize();
if (primVals == null || primVals.length < primDataSize) {
primVals = new byte[primDataSize];
}
desc.getPrimFieldValues(obj, primVals);
bout.write(primVals, 0, primDataSize, false);
ObjectStreamField[] fields = desc.getFields(false);
Object[] objVals = new Object[desc.getNumObjFields()];
int numPrimFields = fields.length - objVals.length;
desc.getObjFieldValues(obj, objVals);
for (int i = 0; i < objVals.length; i++) {
if (extendedDebugInfo) {
debugInfoStack.push(
"field (class \"" + desc.getName() + "\", name: \"" +
fields[numPrimFields + i].getName() + "\", type: \"" +
fields[numPrimFields + i].getType() + "\")");
}
try {
writeObject0(objVals[i],
fields[numPrimFields + i].isUnshared());
} finally {
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
}
}
在此方法中,是系统默认的写入对象的非transient部分。我们在后续的自定义序列化中会做讲解。
五、 我们再来看一下writeExternalData的方法,重要代码如下:
try {
curContext = null;
if (protocol == PROTOCOL_VERSION_1) {
obj.writeExternal(this);
} else {
bout.setBlockDataMode(true);
obj.writeExternal(this);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
}
}
在此方法中obj.writeExternal(this)执行了序列化对象的writeExternal方法,在上述例子中也就是User类的writeExternal方法。
Java的反序列化
一、 与上述的write过程类似,objectInputStream.readObject()方法执行了readObject0(false)方法:主要代码:
switch (tc) {
case TC_NULL:
return readNull();
case TC_REFERENCE:
return readHandle(unshared);
case TC_CLASS:
return readClass(unshared);
case TC_CLASSDESC:
case TC_PROXYCLASSDESC:
return readClassDesc(unshared);
case TC_STRING:
case TC_LONGSTRING:
return checkResolve(readString(unshared));
case TC_ARRAY:
return checkResolve(readArray(unshared));
case TC_ENUM:
return checkResolve(readEnum(unshared));
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));
case TC_EXCEPTION:
IOException ex = readFatalException();
throw new WriteAbortedException("writing aborted", ex);
case TC_BLOCKDATA:
case TC_BLOCKDATALONG:
if (oldMode) {
bin.setBlockDataMode(true);
bin.peek(); // force header read
throw new OptionalDataException(
bin.currentBlockRemaining());
} else {
throw new StreamCorruptedException(
"unexpected block data");
}
case TC_ENDBLOCKDATA:
if (oldMode) {
throw new OptionalDataException(true);
} else {
throw new StreamCorruptedException(
"unexpected end of block data");
}
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
根据不同的对象类型做相应的处理,这里我们关注的是TC_OBJECT,执行的方法是:checkResolve(readOrdinaryObject(unshared));
七、 跟进去,我们可以看到在readOrdinaryObject(unshared)中,执行了以下代码:
private Object readOrdinaryObject(boolean unshared) throws IOException {
if (bin.readByte() != TC_OBJECT) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
desc.checkDeserialize();
Class<?> cl = desc.forClass();
if (cl == String.class || cl == Class.class
|| cl == ObjectStreamClass.class) {
throw new InvalidClassException("invalid class descriptor");
}
Object obj;
try {
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}
passHandle = handles.assign(unshared ? unsharedMarker : obj);
ClassNotFoundException resolveEx = desc.getResolveException();
if (resolveEx != null) {
handles.markException(passHandle, resolveEx);
}
if (desc.isExternalizable()) {
readExternalData((Externalizable) obj, desc);
} else {
readSerialData(obj, desc);
}
handles.finish(passHandle);
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
加载序列化对象的Class,如果可以实例化,那么创建它的实例:desc.newInstance(),User类的无参构造方法执行
自定义Serializable的使用
一、 我们写了Human类,为了方便Main方法也在这里类里面。
package com.huhx.model;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Human implements Serializable{
private static final long serialVersionUID = 1L;
private String username;
private transient String password;
public Human(String username, String password) {
this.username = "not transient " + username;
this.password = "transient " + password;
}
@Override
public String toString() {
return username + "\n" + password;
}
private void readObject(ObjectInputStream inputStream) throws ClassNotFoundException, IOException {
inputStream.defaultReadObject();
password = (String) inputStream.readObject();
}
private void writeObject(ObjectOutputStream outputStream) throws IOException {
outputStream.defaultWriteObject();
outputStream.writeObject(password);
}
public static void main(String[] args) throws Exception {
Human human = new Human("huhx", "liuli");
System.out.println("before: \n" + human);
ByteArrayOutputStream buff = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(buff);
objectOutputStream.writeObject(human);
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(buff.toByteArray()));
Human human2 = (Human) objectInputStream.readObject();
System.out.println("after: \n" + human2);
}
}
定义了两种方法:readObject和writeObject方法,注意命名是有限制的,这个等下解释。好了,我们先看结果
一个String 保持原始状态,其他设为transient(临时),以便证明非临时字段会被defaultWriteObject()方法自动保存,而transient 字段必须在程序中明确保存和恢复。字段是在构建器内部初始化的,而不是在定义的时候,这证明了它们不会在重新装配的时候被某些自动化机制初始化。若准备通过默认机制写入对象的非transient 部分,那么必须调用defaultWriteObject(),令其作为writeObject()中的第一个操作;并调用defaultReadObject(),令其作为readObject()的第一个操作
二、 Human类中的readObject和writeObject方法的执行,不是我们调用的,而是ObjectInputStream与ObjectOutputStream 对象的readObject,writeObject方法去调用的。重要源代码如下
ObjectStreamClass(final Class<?> cl) 构造方法:
if (externalizable) {
cons = getExternalizableConstructor(cl);
} else {
cons = getSerializableConstructor(cl);
writeObjectMethod = getPrivateMethod(cl, "writeObject",
new Class<?>[] { ObjectOutputStream.class },
Void.TYPE);
readObjectMethod = getPrivateMethod(cl, "readObject",
new Class<?>[] { ObjectInputStream.class },
Void.TYPE);
readObjectNoDataMethod = getPrivateMethod(
cl, "readObjectNoData", null, Void.TYPE);
hasWriteObjectData = (writeObjectMethod != null);
}
在writeSerialData的方法当中,有以下的代码:利用反射机制去执行方法
slotDesc.invokeWriteObject(obj, this);