serialization
ObjectOutputStream
The core step
1. Convert objects to binary
/** * Write the specified object to the ObjectOutputStream. The class of the * object, the signature of the class, and the values of the non-transient * and non-static fields of the class and all of its supertypes are * written. Default serialization for a class can be overridden using the * writeObject and the readObject methods. Objects referenced by this * object are written transitively so that a complete equivalent graph of * objects can be reconstructed by an ObjectInputStream. * * <p>Exceptions are thrown for problems with the OutputStream and for * classes that should not be serialized. All exceptions are fatal to the * OutputStream, which is left in an indeterminate state, and it is up to * the caller to ignore or recover the stream state. * *@throws InvalidClassException Something is wrong with a class used by
* serialization.
* @throws NotSerializableException Some object to be serialized does not
* implement the java.io.Serializable interface.
* @throws IOException Any exception thrown by the underlying
* OutputStream.
*/
public final void writeObject(Object obj) throws IOException {
if (enableOverride) {
writeObjectOverride(obj);
return;
}
try {
writeObject0(obj, false); // Call the underlying method
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throwex; }}Copy the code
The core step
- Type of the verification object
- If it is a normal object, the writeOrdinaryObject method is called
/** * Underlying writeObject/writeUnshared implementation. */
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
boolean oldMode = bout.setBlockDataMode(false);
depth++;
try {
// handle previously written and non-replaceable objects
int h;
if ((obj = subs.lookup(obj)) == null) {
writeNull();
return;
} else if(! unshared && (h = handles.lookup(obj)) ! = -1) {
writeHandle(h);
return;
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return;
}
// check for replacement objectObject orig = obj; Class<? > cl = obj.getClass(); ObjectStreamClass desc;for (;;) {
// REMIND: skip this check for strings/arrays?Class<? > repCl; desc = ObjectStreamClass.lookup(cl,true);
if(! desc.hasWriteReplaceMethod() || (obj = desc.invokeWriteReplace(obj)) ==null ||
(repCl = obj.getClass()) == cl)
{
break;
}
cl = repCl;
}
if (enableReplace) {
Object rep = replaceObject(obj);
if(rep ! = obj && rep ! =null) {
cl = rep.getClass();
desc = ObjectStreamClass.lookup(cl, true);
}
obj = rep;
}
// if object replaced, run through original checks a second time
if(obj ! = orig) { subs.assign(orig, obj);if (obj == null) {
writeNull();
return;
} else if(! unshared && (h = handles.lookup(obj)) ! = -1) {
writeHandle(h);
return;
} else if (obj instanceof Class) {
writeClass((Class) obj, unshared);
return;
} else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
return; }}// Remaining cases // Remaining cases
// The type of the verification object
if (obj instanceof String) { / / string
writeString((String) obj, unshared);
} else if (cl.isArray()) { / / array
writeArray(obj, desc, unshared);
} else if (obj instanceofEnum) { writeEnum((Enum<? >) obj, desc, unshared); }else if (obj instanceof Serializable) { // serializable
// Write ordinary objects
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw newNotSerializableException(cl.getName()); }}}finally{ depth--; bout.setBlockDataMode(oldMode); }}Copy the code
The core step
- Write the class description
- Convert objects to binary
/** * Writes representation of a "ordinary" (i.e., not a String, Class, * ObjectStreamClass, array, or enum constant) serializable object to the * stream. */
private void writeOrdinaryObject(Object obj,
ObjectStreamClass desc,
boolean unshared)
throws IOException
{
if (extendedDebugInfo) {
debugInfoStack.push(
(depth == 1 ? "root " : "") + "object (class "" + obj.getClass().getName() + ""," + obj.toString() + ")");
}
try {
desc.checkSerialize();
bout.writeByte(TC_OBJECT);
// Write the class description
writeClassDesc(desc, false);
handles.assign(unshared ? null : obj);
if(desc.isExternalizable() && ! desc.isProxy()) { writeExternalData((Externalizable) obj); }else {
// Convert the object to binarywriteSerialData(obj, desc); }}finally {
if(extendedDebugInfo) { debugInfoStack.pop(); }}}Copy the code
The core step
1. Convert objects to binary
/** * Writes instance data for each serializable class of given object, from * superclass to subclass. */
private void writeSerialData(Object obj // ObjectStreamClass desc)
throws IOException
{
ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
for (int i = 0; i < slots.length; i++) {
ObjectStreamClass slotDesc = slots[i].desc;
if (slotDesc.hasWriteObjectMethod()) {
PutFieldImpl oldPut = curPut;
curPut = null;
SerialCallbackContext oldContext = curContext;
if (extendedDebugInfo) {
debugInfoStack.push(
"custom writeObject data (class "" + slotDesc.getName() + "")");
}
try {
curContext = new SerialCallbackContext(obj, slotDesc);
bout.setBlockDataMode(true);
slotDesc.invokeWriteObject(obj, this);
bout.setBlockDataMode(false);
bout.writeByte(TC_ENDBLOCKDATA);
} finally {
curContext.setUsed();
curContext = oldContext;
if (extendedDebugInfo) {
debugInfoStack.pop();
}
}
curPut = oldPut;
} else {
// Convert the object to binarydefaultWriteFields(obj, slotDesc); }}}Copy the code
The core step
- Write the basic data type first
- Write non-basic data types
/** * Fetches and writes values of serializable fields of given object to * stream. The given class descriptor specifies which field values to * write, and in which order they should be written. */
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();
// Write the underlying data type first
int primDataSize = desc.getPrimDataSize(); // Get the underlying data type
if (primVals == null || primVals.length < primDataSize) {
primVals = new byte[primDataSize];
}
desc.getPrimFieldValues(obj, primVals);
bout.write(primVals, 0, primDataSize, false);
// Write the non-base data type again
ObjectStreamField[] fields = desc.getFields(false); // Get non-base data type fields
Object[] objVals = new Object[desc.getNumObjFields()];
int numPrimFields = fields.length - objVals.length;
desc.getObjFieldValues(obj, objVals);
for (int i = 0; i < objVals.length; i++) { // Loop through non-base data type fields
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()); // Write non-basic data type fields
} finally {
if(extendedDebugInfo) { debugInfoStack.pop(); }}}}Copy the code
deserialization
ObjectInputStream
To read an object, the underlying call is the readObject0 method.
/**
* Read an object from the ObjectInputStream. The class of the object, the
* signature of the class, and the values of the non-transient and
* non-static fields of the class and all of its supertypes are read.
* Default deserializing for a class can be overridden using the writeObject
* and readObject methods. Objects referenced by this object are read
* transitively so that a complete equivalent graph of objects is
* reconstructed by readObject.
*
* <p>The root object is completely restored when all of its fields and the
* objects it references are completely restored. At this point the object
* validation callbacks are executed in order based on their registered
* priorities. The callbacks are registered by objects (in the readObject
* special methods) as they are individually restored.
*
* <p>Exceptions are thrown for problems with the InputStream and for
* classes that should not be deserialized. All exceptions are fatal to
* the InputStream and leave it in an indeterminate state; it is up to the
* caller to ignore or recover the stream state.
*
* @throws ClassNotFoundException Class of a serialized object cannot be
* found.
* @throws InvalidClassException Something is wrong with a class used by
* serialization.
* @throws StreamCorruptedException Control information in the
* stream is inconsistent.
* @throws OptionalDataException Primitive data was found in the
* stream instead of objects.
* @throws IOException Any of the usual Input/Output related exceptions.
*/
public final Object readObject(a)
throws IOException, ClassNotFoundException
{
if (enableOverride) {
return readObjectOverride();
}
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false); // The underlying method of reading objects
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(); }}}Copy the code
The core step
- Type of the verification object
Is it a string? An array? Or ordinary objects?
- If it is a normal object, the readOrdinaryObject method is called
/** * 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 {
// What type is the verification object
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: / / string
case TC_LONGSTRING:
return checkResolve(readString(unshared));
case TC_ARRAY: / / array
return checkResolve(readArray(unshared));
case TC_ENUM:
return checkResolve(readEnum(unshared));
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared)); // Read ordinary objects
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); }}Copy the code
The core step
- Read the fields of the object
- Create an object
/** * Reads and returns "ordinary" (i.e., not a String, Class, * ObjectStreamClass, array, or enum constant) object, or null if object's * class is unresolvable (in which case a ClassNotFoundException will be * associated with object's handle). Sets passHandle to object's assigned * handle. */
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
if(bin.readByte() ! = TC_OBJECT) {throw new InternalError();
}
// Read the fields of the object
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 {
// Create an object if it can be instantiated
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) {// 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;
}
Copy the code
ObjectStreamClass
The core step
- Create an object
/** * 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(a)
throws InstantiationException, InvocationTargetException,
UnsupportedOperationException
{
requireInitialized();
if(cons ! =null) {
try {
if (domains == null || domains.length == 0) {
return cons.newInstance(); // Create an object
} else{ JavaSecurityAccess jsa = SharedSecrets.getJavaSecurityAccess(); PrivilegedAction<? > pea = () -> {try {
return cons.newInstance();
} catch (InstantiationException
| InvocationTargetException
| IllegalAccessException x) {
throw newUndeclaredThrowableException(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
throwx; }}}catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw newInternalError(ex); }}else {
throw newUnsupportedOperationException(); }}Copy the code
The bottom line is to call the constructor’s newInstance method to create an object, and the bottom line is to call the native method:
NativeConstructorAccessorImpl class
private static native Object newInstance0(Constructor
var0, Object[] var1) throws InstantiationException, IllegalArgumentException, InvocationTargetException;
Copy the code
reference
Developer.aliyun.com/article/643…
Zhzhdoai. Making. IO / 2020/02/07 /…