衆所周知,Java 原生的序列化方法可以分爲兩種:
- 實現 Serializable 接口:可以自定義 writeObject、readObject、writeReplace、readResolve 方法,會通過反射調用。
- 實現 Externalizable 接口:需要實現 writeExternal 和 readExternal 方法。
實際上,Externalizable接口繼承自Serializable接口,但他們的序列化機制是完全不同的:使用Serializable的方式,在反序列化時不會直接調用被序列化對象的構造器,而是先獲取被序列化對象對應類的 【自下而上最頂層實現了Serializable的祖先類的超類】【即自上而下連續的最後一個未實現Serizable接口的類】的構造器,然後在此構造器的基礎上重新創建一個新的構造器來完成實例化。這句話讀起來有些拗口,我們後面分析Serializable反序列化機制時還會詳細介紹。而使用Externalizable則是調用一個無參構造方法來實例化,原因如下:
Externalizable序列化的過程:使用Externalizable序列化時,在進行反序列化的時候,會重新實例化一個對象,然後再將被反序列化的對象的狀態全部複製到這個新的實例化對象當中去,這也就是爲什麼會調用構造方法啦,也因此必須有一個無參構造方法供其調用,並且權限是public。
對象反序列化時通過構造函數來實例化對象是很直觀的容易理解的方式,而實現Serializable接口的方式在反序列化時卻不會直接調用被序列化對象的構造器,JVM爲什麼要這麼做呢?個人認爲是爲了讓一些沒有無參構造方法的類也能實現序列化和反序列化(Gson sun.misc.Unsafe.allocateInstance序列化Java對象這篇博文中就講到,Fastjson和Jackson都無法將沒有無參構造方法的類對象反序列化,而Gson底層通過調用Unsafe.allocateInstance方法來反序列化沒有無參構造方法的類對象。這纔是真正意義上的不使用構造器實現類對象反序列化的機制,後面還會再介紹)。下面通過分析ObjectInputStream的源碼來梳理實現Serializable接口的反序列化方式。
我們通過ObjectInputStream的readObject()方法來實現對象的反序列化,下面爲該方法的源碼:
public final Object readObject()
throws IOException, ClassNotFoundException
{
if (enableOverride) {
return readObjectOverride();
}
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false); //實際的實現方法
handles.markDependency(outerHandle, passHandle);
ClassNotFoundException ex = handles.lookupException(passHandle);
if (ex != null) {
throw ex;
}
if (depth == 0) {
vlist.doCallbacks();
}
return obj;
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
clear();
}
}
}
其中readObject0(false)爲readObject()的實際實現方法:
/**
* Underlying readObject implementation.
*/
private Object readObject0(boolean unshared) throws IOException {
boolean oldMode = bin.getBlockDataMode();
if (oldMode) {
int remain = bin.currentBlockRemaining();
if (remain > 0) {
throw new OptionalDataException(remain);
} else if (defaultDataEnd) {
/*
* Fix for 4360508: stream is currently at the end of a field
* value block written via default serialization; since there
* is no terminating TC_ENDBLOCKDATA tag, simulate
* end-of-custom-data behavior explicitly.
*/
throw new OptionalDataException(true);
}
bin.setBlockDataMode(false);
}
byte tc; //存儲對象類型標誌
while ((tc = bin.peekByte()) == TC_RESET) {
bin.readByte();
handleReset();
}
depth++;
totalObjectRefs++;
try {
switch (tc) { //按存儲對象類型標誌分case讀取
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));
}
} finally {
depth--;
bin.setBlockDataMode(oldMode);
}
}
該方法先從輸入流中讀取對象的類型標誌,以普通對象Object爲例,會調用java.io.ObjectInputStream.checkResolve(java.lang.Object),而在上面的代碼中可以看到,checkResolve()方法的入參爲readOrdinaryObject(unshared),實際上就是在readOrdinaryObject(boolean)方法中完成的實例化。我們接着看readOrdinaryObject(boolean)的源碼:
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_OBJECT) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false); //1.讀取類描述信息
desc.checkDeserialize();//2.check該類能否反序列化,判斷條件爲是否實現Serializable或Externalizable其一
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;//3.實例化對象
} 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); //4.對象賦值
} else {
readSerialData(obj, desc); //4.對象賦值
}
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) {
// Filter the replacement object
if (rep != null) {
if (rep.getClass().isArray()) {
filterCheck(rep.getClass(), Array.getLength(rep));
} else {
filterCheck(rep.getClass(), -1);
}
}
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
在上述代碼中最關鍵的兩個步驟:1.讀取類描述信息;3.實例化對象。爲了便於理解,我們先來看第三步中實例化對象的方法desc.newInstance()。該方法在類描述信息java.io.ObjectStreamClass類中。
/**
* Creates a new instance of the represented class. If the class is
* externalizable, invokes its public no-arg constructor; otherwise, if the
* class is serializable, invokes the no-arg constructor of the first
* non-serializable superclass. Throws UnsupportedOperationException if
* this class descriptor is not associated with a class, if the associated
* class is non-serializable or if the appropriate no-arg constructor is
* inaccessible/unavailable.
*/
Object newInstance()
throws InstantiationException, InvocationTargetException,
UnsupportedOperationException
{
requireInitialized();
if (cons != null) {
try {
if (domains == null || domains.length == 0) {
return cons.newInstance(); //調用構造器實例化對象
} else {
JavaSecurityAccess jsa = SharedSecrets.getJavaSecurityAccess();
PrivilegedAction<?> pea = () -> {
try {
return cons.newInstance();
} catch (InstantiationException
| InvocationTargetException
| IllegalAccessException x) {
throw new UndeclaredThrowableException(x);
}
}; // Can't use PrivilegedExceptionAction with jsa
try {
return jsa.doIntersectionPrivilege(pea,
AccessController.getContext(),
new AccessControlContext(domains));
} catch (UndeclaredThrowableException x) {
Throwable cause = x.getCause();
if (cause instanceof InstantiationException)
throw (InstantiationException) cause;
if (cause instanceof InvocationTargetException)
throw (InvocationTargetException) cause;
if (cause instanceof IllegalAccessException)
throw (IllegalAccessException) cause;
// not supposed to happen
throw x;
}
}
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw new InternalError(ex);
}
} else {//構造器爲空則拋異常,反序列化失敗
throw new UnsupportedOperationException();
}
}
由上述代碼可知,desc.newInstance()方法通過調用cons.newInstance()方法完成實例化。其實,從該方法上面的那段註釋我們可以瞭解到,如果反序列化類實現了externalizable,則這裏調用的就是權限爲public的無參構造函數;否則如果反序列化類實現了serializable,則這裏調用的就是第一個沒有實現serializable接口的父類的無參構造器。
到這裏我們可以確認的是,實現Serializable接口的方式反序列化本質上同樣是利用構造器來完成的實例化。而爲了證實代碼註釋中所說的事實,我們需要回過頭去看第一步中讀取類描述信息的readClassDesc(boolean unshared)方法:
private ObjectStreamClass readClassDesc(boolean unshared)
throws IOException
{
byte tc = bin.peekByte();
ObjectStreamClass descriptor;
switch (tc) {
case TC_NULL:
descriptor = (ObjectStreamClass) readNull();
break;
case TC_REFERENCE:
descriptor = (ObjectStreamClass) readHandle(unshared);
break;
case TC_PROXYCLASSDESC:
descriptor = readProxyDesc(unshared);//讀取代理類描述信息
break;
case TC_CLASSDESC:
descriptor = readNonProxyDesc(unshared);//讀取非代理類描述信息
break;
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
if (descriptor != null) {
validateDescriptor(descriptor);
}
return descriptor;
}
以讀取非代理類描述信息方法readNonProxyDesc(boolean)爲例:
private ObjectStreamClass readNonProxyDesc(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_CLASSDESC) {
throw new InternalError();
}
ObjectStreamClass desc = new ObjectStreamClass();
int descHandle = handles.assign(unshared ? unsharedMarker : desc);
passHandle = NULL_HANDLE;
ObjectStreamClass readDesc = null;
try {
readDesc = readClassDescriptor();
} catch (ClassNotFoundException ex) {
throw (IOException) new InvalidClassException(
"failed to read class descriptor").initCause(ex);
}
Class<?> cl = null;
ClassNotFoundException resolveEx = null;
bin.setBlockDataMode(true);
final boolean checksRequired = isCustomSubclass();
try {
if ((cl = resolveClass(readDesc)) == null) {
resolveEx = new ClassNotFoundException("null class");
} else if (checksRequired) {
ReflectUtil.checkPackageAccess(cl);
}
} catch (ClassNotFoundException ex) {
resolveEx = ex;
}
// Call filterCheck on the class before reading anything else
filterCheck(cl, -1);
skipCustomData();
try {
totalObjectRefs++;
depth++;
desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));//初始化非代理類描述對象
} finally {
depth--;
}
handles.finish(descHandle);
passHandle = descHandle;
return desc;
}
我們接着看initNonProxy(ObjectStreamClass model, Class<?> cl, ClassNotFoundException resolveEx, ObjectStreamClass superDesc)方法:
void initNonProxy(ObjectStreamClass model,
Class<?> cl,
ClassNotFoundException resolveEx,
ObjectStreamClass superDesc)
throws InvalidClassException
{
long suid = Long.valueOf(model.getSerialVersionUID());
ObjectStreamClass osc = null;
if (cl != null) {
osc = lookup(cl, true); //查找並返回非代理類描述對象
if (osc.isProxy) {
throw new InvalidClassException(
"cannot bind non-proxy descriptor to a proxy class");
}
if (model.isEnum != osc.isEnum) {
throw new InvalidClassException(model.isEnum ?
"cannot bind enum descriptor to a non-enum class" :
"cannot bind non-enum descriptor to an enum class");
}
if (model.serializable == osc.serializable &&
!cl.isArray() &&
suid != osc.getSerialVersionUID()) {
throw new InvalidClassException(osc.name,
"local class incompatible: " +
"stream classdesc serialVersionUID = " + suid +
", local class serialVersionUID = " +
osc.getSerialVersionUID());
}
if (!classNamesEqual(model.name, osc.name)) {
throw new InvalidClassException(osc.name,
"local class name incompatible with stream class " +
"name \"" + model.name + "\"");
}
if (!model.isEnum) {
if ((model.serializable == osc.serializable) &&
(model.externalizable != osc.externalizable)) {//一個類不能同時實現serializable和externalizable
throw new InvalidClassException(osc.name,
"Serializable incompatible with Externalizable");
}
if ((model.serializable != osc.serializable) ||
(model.externalizable != osc.externalizable) ||
!(model.serializable || model.externalizable)) {//如果一個類既沒實現serializable,也沒實現externalizable,則給該類的描述對象的成員變量deserializeEx賦值,該值不爲空則標誌該類不能反序列化
deserializeEx = new ExceptionInfo(
osc.name, "class invalid for deserialization");
}
}
}
this.cl = cl;
this.resolveEx = resolveEx;
this.superDesc = superDesc;
name = model.name;
this.suid = suid;
isProxy = false;
isEnum = model.isEnum;
serializable = model.serializable;
externalizable = model.externalizable;
hasBlockExternalData = model.hasBlockExternalData;
hasWriteObjectData = model.hasWriteObjectData;
fields = model.fields;
primDataSize = model.primDataSize;
numObjFields = model.numObjFields;
if (osc != null) {
localDesc = osc;
writeObjectMethod = localDesc.writeObjectMethod;
readObjectMethod = localDesc.readObjectMethod;
readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
writeReplaceMethod = localDesc.writeReplaceMethod;
readResolveMethod = localDesc.readResolveMethod;
if (deserializeEx == null) {
deserializeEx = localDesc.deserializeEx;
}
domains = localDesc.domains;
cons = localDesc.cons;
}
fieldRefl = getReflector(fields, localDesc);
// reassign to matched fields so as to reflect local unshared settings
fields = fieldRefl.getFields();
initialized = true;
}
我們接着看lookup(Class<?> cl, boolean all)方法:
static ObjectStreamClass lookup(Class<?> cl, boolean all) {
if (!(all || Serializable.class.isAssignableFrom(cl))) {
return null;
}
processQueue(Caches.localDescsQueue, Caches.localDescs);
WeakClassKey key = new WeakClassKey(cl, Caches.localDescsQueue);
Reference<?> ref = Caches.localDescs.get(key);
Object entry = null;
if (ref != null) {
entry = ref.get();
}
EntryFuture future = null;
if (entry == null) {
EntryFuture newEntry = new EntryFuture();
Reference<?> newRef = new SoftReference<>(newEntry);
do {
if (ref != null) {
Caches.localDescs.remove(key, ref);
}
ref = Caches.localDescs.putIfAbsent(key, newRef);
if (ref != null) {
entry = ref.get();
}
} while (ref != null && entry == null);
if (entry == null) {
future = newEntry;
}
}
if (entry instanceof ObjectStreamClass) { // check common case first
return (ObjectStreamClass) entry;
}
if (entry instanceof EntryFuture) {
future = (EntryFuture) entry;
if (future.getOwner() == Thread.currentThread()) {
/*
* Handle nested call situation described by 4803747: waiting
* for future value to be set by a lookup() call further up the
* stack will result in deadlock, so calculate and set the
* future value here instead.
*/
entry = null;
} else {
entry = future.get();
}
}
if (entry == null) {
try {
entry = new ObjectStreamClass(cl); //1.實例化類描述對象
} catch (Throwable th) {
entry = th;
}
if (future.set(entry)) {
Caches.localDescs.put(key, new SoftReference<Object>(entry));
} else {
// nested lookup call already set future
entry = future.get();
}
}
if (entry instanceof ObjectStreamClass) {
return (ObjectStreamClass) entry; //2.返回類描述對象
} else if (entry instanceof RuntimeException) {
throw (RuntimeException) entry;
} else if (entry instanceof Error) {
throw (Error) entry;
} else {
throw new InternalError("unexpected entry: " + entry);
}
}
在上述代碼中,先調用構造函數實例化反序列化對象的類描述對象,並返回。我們來看類描述對象的構造函數ObjectStreamClass(final Class<?> cl):
private ObjectStreamClass(final Class<?> cl) {
this.cl = cl;
name = cl.getName();
isProxy = Proxy.isProxyClass(cl);
isEnum = Enum.class.isAssignableFrom(cl);
serializable = Serializable.class.isAssignableFrom(cl);
externalizable = Externalizable.class.isAssignableFrom(cl);
Class<?> superCl = cl.getSuperclass();
superDesc = (superCl != null) ? lookup(superCl, false) : null;
localDesc = this;
if (serializable) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
if (isEnum) {
suid = Long.valueOf(0);
fields = NO_FIELDS;
return null;
}
if (cl.isArray()) {
fields = NO_FIELDS;
return null;
}
suid = getDeclaredSUID(cl);
try {
fields = getSerialFields(cl);
computeFieldOffsets();
} catch (InvalidClassException e) {
serializeEx = deserializeEx =
new ExceptionInfo(e.classname, e.getMessage());
fields = NO_FIELDS;
}
if (externalizable) {
cons = getExternalizableConstructor(cl);//獲取Externalizable方式的構造器
} else {
cons = getSerializableConstructor(cl); //獲取Serilizable機制的構造器
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);
}
domains = getProtectionDomains(cons, cl);
writeReplaceMethod = getInheritableMethod(
cl, "writeReplace", null, Object.class);
readResolveMethod = getInheritableMethod(
cl, "readResolve", null, Object.class);
return null;
}
});
} else {
suid = Long.valueOf(0);
fields = NO_FIELDS;
}
try {
fieldRefl = getReflector(fields, this);
} catch (InvalidClassException ex) {
// field mismatches impossible when matching local fields vs. self
throw new InternalError(ex);
}
if (deserializeEx == null) {
if (isEnum) {
deserializeEx = new ExceptionInfo(name, "enum type");
} else if (cons == null) {
deserializeEx = new ExceptionInfo(name, "no valid constructor");
}
}
for (int i = 0; i < fields.length; i++) {
if (fields[i].getField() == null) {
defaultSerializeEx = new ExceptionInfo(
name, "unmatched serializable field(s) declared");
}
}
initialized = true;
}
我們重點看下獲取Serializable機制的構造器方法getSerializableConstructor(Class<?> cl):
private static Constructor<?> getSerializableConstructor(Class<?> cl) {
Class<?> initCl = cl;
while (Serializable.class.isAssignableFrom(initCl)) {//1.自底向上查找反序列化類的父類中第一個沒有繼承Serializable的類
Class<?> prev = initCl;
if ((initCl = initCl.getSuperclass()) == null ||
(!disableSerialConstructorChecks && !superHasAccessibleConstructor(prev))) {
return null;
}
}
try {
Constructor<?> cons = initCl.getDeclaredConstructor((Class<?>[]) null); //2.獲取父類中第一個沒有繼承Serializable的類的無參構造器,如果父類沒有無參構造器則拋異常
int mods = cons.getModifiers();
if ((mods & Modifier.PRIVATE) != 0 ||
((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 &&
!packageEquals(cl, initCl)))
{
return null;
}
cons = reflFactory.newConstructorForSerialization(cl, cons);//3.基於父類中第一個沒有繼承Serializable的類的構造器產生一個新的構造器
cons.setAccessible(true);
return cons;
} catch (NoSuchMethodException ex) {
return null;//父類沒有無參構造器則返回null,最終會導致類實例化失敗
}
}
在上述代碼中,重點有三步:第一步,自底向上查找反序列化類的父類中第一個沒有繼承Serializable的類;第二步,獲取第一步中得到的父類無參構造器;第三步,基於第二步得到的無參構造器產生一個新的構造器。我們重點來看第三步(sun.reflect.ReflectionFactory中的newConstructorForSerialization()方法):
public Constructor<?> newConstructorForSerialization(Class<?> var1, Constructor<?> var2) {
return var2.getDeclaringClass() == var1 ? var2 : this.generateConstructor(var1, var2);
}
實際上,上述三元表達式中var2.getDeclaringClass() == var1標誌着當前類沒有實現Serializable接口,不在正常情況的考慮範圍,因而我們重點看generateConstructor(Class<?> var1, Constructor<?> var2)方法:
private final Constructor<?> generateConstructor(Class<?> var1, Constructor<?> var2) {
SerializationConstructorAccessorImpl var3 = (new MethodAccessorGenerator()).generateSerializationConstructor(var1, var2.getParameterTypes(), var2.getExceptionTypes(), var2.getModifiers(), var2.getDeclaringClass());
Constructor var4 = this.newConstructor(var2.getDeclaringClass(), var2.getParameterTypes(), var2.getExceptionTypes(), var2.getModifiers(), langReflectAccess().getConstructorSlot(var2), langReflectAccess().getConstructorSignature(var2), langReflectAccess().getConstructorAnnotations(var2), langReflectAccess().getConstructorParameterAnnotations(var2));
this.setConstructorAccessor(var4, var3);
var4.setAccessible(true);
return var4;
}
由該方法可知,我們是將父類的無參構造器作爲入參,重新創建一個包含該構造器的新構造器並返回。
總結:
Java 原生的序列化方法可以分爲兩種,即
- 實現 Serializable 接口:可以自定義 writeObject、readObject、writeReplace、readResolve 方法,會通過反射調用。
- 實現 Externalizable 接口:需要實現 writeExternal 和 readExternal 方法。
兩者的序列化機制是完全不同的:使用Serializable的方式,在反序列化時不會直接調用被序列化對象的構造器,而是先獲取被序列化對象對應類的 【自下而上最頂層實現了Serializable的祖先類的超類】【即自上而下連續的最後一個未實現Serizable接口的類】的構造器,然後在此構造器的基礎上重新創建一個新的構造器來完成實例化。而使用Externalizable是通過調用一個無參構造方法來實例化。
本質上兩種方式都是使用類構造器來完成實例化,相對於Externalizable必須要求類實現無參構造器,Serializable放開了這一限制,在一定程度上適用性強於Externalizable,但仍然要求其未實現Serializable接口的父類一定要實現無參構造器,否則還是無法反序列化。另一方面,實現 Externalizable 接口能帶來較大的時間及空間的性能提升(相比於Serializable需要構造新的構造器等複雜邏輯,具體參見幾種序列化協議的介紹),但由於實現 Externalizable 接口導致了編程複雜度的增加,所以大部分時候都是採用實現 Serializable 接口方式來實現序列化。
此外,還可以通過使用Unsafe的allocateInstance()方法,其實現真正做到了不依賴構造器實現類的實例化。allocateInstance方法爲native方法,基於C語言實現,感興趣的可以閱讀參考博客:不調用給定類的構造方法創建給定類的對象和源碼分析:Java對象的內存分配。
參考博客:
1、https://blog.csdn.net/baiye_xing/article/details/71809993 【Java深入】序列化詳解
2、https://blog.csdn.net/sym90/article/details/41940341 Java 學習之路 之 對象序列化(六十九)
3、https://blog.csdn.net/u013815832/article/details/95212567 Java 序列化和反序列化(一)Serializable 使用場景
4、https://my.oschina.net/valsong/blog/2248616 java 通過Unsafe不使用構造器直接創建對象
5、http://javaweb.org/?p=1872 Gson sun.misc.Unsafe.allocateInstance序列化Java對象
6、 https://blog.csdn.net/amen_wu/article/details/80578849 不通過構造函數也能創建對象嗎?
7、https://blog.csdn.net/xlgen157387/article/details/79840134 序列化和反序列化的底層實現原理是什麼?
8、https://blog.csdn.net/u014653197/article/details/78114041 JAVA對象流序列化時的readObject,writeObject,readResolve是怎麼被調用的
9、https://www.iteye.com/blog/yueyemaitian-2078090 JDK中反序列化對象的過程(ObjectInputStream#readObject)
10、https://blog.csdn.net/zyzzxycj/article/details/89877863 深入理解sun.misc.Unsafe原理
11、https://www.jb51.net/article/140726.htm 一篇看懂Java中的Unsafe類
12、https://blog.csdn.net/weixin_41050155/article/details/82960099 創建對像實例的5種方式
13、https://blog.csdn.net/u011039332/article/details/91345986 不調用給定類的構造方法創建給定類的對象 包含序列化字節示例
14、https://www.cppentry.com/bencandy.php?fid=54&aid=111020&page=2 源碼分析:Java對象的內存分配
15、https://www.cnblogs.com/grl214/p/5895854.html java構造器的深入理解
16、https://blog.csdn.net/lb_383691051/article/details/46582109?ref=myread Java構造器(定義,作用,原理)
17、https://blog.csdn.net/baiye_xing/article/details/73249819 幾種序列化協議的介紹 有各種協議的時間及空間性能對比
18、https://www.cnblogs.com/senlinyang/p/8204752.html Java序列化機制原理