java高級---->Serializable的過程分析


  本次講解中我們在上次的基礎上,深入的了解一下序列化的流程以及其中的原理。關於序列化的一些知識與使用,請參見我的另一篇博客:java基礎---->Serializable的使用。好了,我們進行以下分析的講解。

 

目錄導航

  1.   Java序列化的原理分析
  2.   自定義Serializable的使用
  3.   友情鏈接

 

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);
}
slotDesc.invokeWriteObject(obj, this);

 

友情鏈接

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM