Java反序列化創建對象探析


通過反序列化生成對象的過程主要由以下幾個步驟:

1、創建一個對象輸入流,它可以包裝一個其他類型的源輸入流,如文件輸入流;

2、 通過對象輸入流的readObject()方法讀取對象。

其中正是readObject方法返回了一個對象,這個對象就是根據序列化生成的文件而創建的對象,所以反序列化如何創建對象關鍵就在於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();
            }
        }
    }

這個方法會從對象輸入流中讀取一個對象,其中還包括這個對象的類名、類的簽名、類的非靜態非瞬時屬性值、以及它的所有超類。

該對象所引用的對象是傳遞式讀取的,所以readObject方法可以重建對象的完整等效圖,也就復刻一個和原來被序列化對象一樣的對象。

由於該方法返回值就是一個Object類型,所以我們重點看實際返回的obj這個對象是怎么創建,可以看出它是通過另一個方法創建,繼續查看readObject0方法的源碼:

 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 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);
        }
    }

這個方法是readObject方法的底層實現,由於它的返回值類型也是對象類型,所以我們重點看返回的實際對象是哪個,通過觀察和debug發現,開關語句執行時下面這行代碼,checkResolve方法接收的參數類型是對象類型,返回值類型也是對象類型。

case TC_OBJECT:
         return checkResolve(readOrdinaryObject(unshared));

那我們來查看readOrdinaryObject對象:

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) {
            // 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;
}

此處我們重點看的還應該是產生實際返回值的地方,也就是這塊:

obj = desc.isInstantiable() ? desc.newInstance() : null;

desc.newInstance()方法原理是利用反射創建了一個對象,本質是調用非序列化父類的無參構造器。

如果該類是Externalizable類型的,則調用它自身的訪問權限是public無參構造方法。

如果該類是Serializable類型的,則調用該類的第一個非Serializable類型的父類的無參構造方法。

所以這就是為什么通過反序列化創建對象的時候,並不會執行被序列化對象的構造方法。

對於實現Serializable接口的類,並不要求該類具有一個無參的構造方法, 因為在反序列化的過程中實際上是去其繼承樹上找到一個沒有實現Serializable接口的父類(最終會找到Object),然后構造該類的對象,再逐層往下的去設置各個可以反序列化的屬性(也就是沒有被transient修飾的非靜態屬性)。

我們通過一個具體的案例就可以看出來:

 
import java.io.Serializable;

public class Users implements Serializable {
    private String username;
    private int age;
    private String sex;

    public Users() {
        System.out.println("調用了構造方法");
    }
  /*
    get和set方法
  */

  …………
}

創建一個測試類:

import java.io.*;

public class TestUsers {
    public static void main(String[] args) {

        try {
            System.out.println("開始序列化。。。。。");
            Users users = new Users();
            users.setUsername("fym");
            users.setAge(23);
            users.setSex("nan");

            FileOutputStream fs = new FileOutputStream("users.ser");
            ObjectOutputStream os = new ObjectOutputStream(fs);
            os.writeObject(users);
            os.close();
            fs.close();

            System.out.println("開始反序列化。。。。。");
            FileInputStream fileInputStream = new FileInputStream("users.ser");
            ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
            Users o = (Users)objectInputStream.readObject();
            System.out.println(o);
            fileInputStream.close();
            objectInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

輸出結果:

開始序列化。。。。。
調用了構造方法
開始反序列化。。。。。
Users{username='fym', age=23, sex='nan'}

我們將其改造一下,讓User類繼承一個沒有序列化的父類:

class ParentsUser{
    public ParentsUser(){
        System.out.println("調用了父類構造方法");
    }
}
public class Users extends ParentsUser implements Serializable {
    ……
}    

再一次執行上面的測試類,輸出結果如下:

開始序列化。。。。。
調用了父類構造方法
調用了構造方法
開始反序列化。。。。。
調用了父類構造方法
Users{username='fym', age=23, sex='nan'}

發現,反序列化創建對象的時候,果然調用了離User類最近的沒有序列化的超類的無參構造函數。


免責聲明!

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



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