亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java序列化機(jī)制詳解

 更新時(shí)間:2023年12月19日 08:43:02   作者:修己xj  
Java 序列化機(jī)制是一種將對象轉(zhuǎn)換為字節(jié)流的過程,以便在網(wǎng)絡(luò)上傳輸或保存到文件中,并能在需要時(shí)將字節(jié)流還原為對象,這一機(jī)制通過實(shí)現(xiàn) java.io.Serializable 接口來實(shí)現(xiàn),同時(shí)涉及到一些關(guān)鍵概念和注意事項(xiàng),需要的朋友可以參考下

Serializable 接口

Serializable 接口是 Java 提供的標(biāo)記接口,沒有包含任何需要實(shí)現(xiàn)的方法。實(shí)現(xiàn)了這個接口的類表明其對象是可序列化的,可以被轉(zhuǎn)換為字節(jié)流。

public interface Serializable {
}

通過實(shí)現(xiàn) Serializable 接口,標(biāo)識類的對象可以被序列化。這使得對象可以在網(wǎng)絡(luò)上傳輸或保存到文件中,而不失去其狀態(tài)和結(jié)構(gòu)。

序列化過程

序列化是將對象的狀態(tài)(字段值)轉(zhuǎn)換為字節(jié)流的過程。這個過程由 ObjectOutputStream 類來完成。序列化使得對象可以以字節(jié)流的形式進(jìn)行存儲或傳輸,便于在不同系統(tǒng)之間進(jìn)行數(shù)據(jù)交換。如下我們列舉幾個重要的方法的源碼:

  • writeObject方法

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

enableOverride 表示是否啟用了對象寫入的覆蓋機(jī)制。如果啟用,會調(diào)用 writeObjectOverride 方法來執(zhí)行對象的特定寫入邏輯。

如果沒有啟用覆蓋機(jī)制,則調(diào)用 writeObject0 方法執(zhí)行實(shí)際的對象序列化。

writeObject0 方法負(fù)責(zé)處理對象的序列化,其中第二個參數(shù) false 表示不使用不共享的方式進(jìn)行序列化。

如果在序列化過程中拋出 IOException 異常,會捕獲該異常。如果當(dāng)前深度為0(表示不在嵌套序列化過程中),則調(diào)用 writeFatalException 方法來處理異常,否則將異常重新拋出。

  • writeObject0

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 object
        Object 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
        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());
            }
        }
    } finally {
        depth--;
        bout.setBlockDataMode(oldMode);
    }
}

反序列化過程

當(dāng)需要從字節(jié)流中恢復(fù)對象時(shí),Java 序列化機(jī)制會將字節(jié)流還原為對象的狀態(tài)。這個過程由 ObjectInputStream 類來完成。如下我們列舉幾個重要的方法的源碼:

  • readObject()

private final Object readObject(Class<?> type)
    throws IOException, ClassNotFoundException
{
    if (enableOverride) {
        return readObjectOverride();
    }

    if (! (type == Object.class || type == String.class))
        throw new AssertionError("internal error");

    // if nested read, passHandle contains handle of enclosing object
    int outerHandle = passHandle;
    try {
        Object obj = readObject0(type, 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();
        }
    }
}

這段代碼的主要作用是根據(jù)給定的類型 (type) 進(jìn)行對象的反序列化。在這個過程中,它使用了一些狀態(tài)變量,如 enableOverride、passHandle、handles、depth、vlist 等,來管理反序列化的過程。在處理嵌套對象時(shí),它通過 markDependency 方法標(biāo)記了當(dāng)前對象與封閉對象的依賴關(guān)系。在深度為 0 時(shí),執(zhí)行了clear方法。具體反序列化執(zhí)行的核心方法是readObject0()

  • readObject0()

private Object readObject0(Class<?> type, 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:
                // check the type of the existing object
                return type.cast(readHandle(unshared));

            case TC_CLASS:
                if (type == String.class) {
                    throw new ClassCastException("Cannot cast a class to java.lang.String");
                }
                return readClass(unshared);

            case TC_CLASSDESC:
            case TC_PROXYCLASSDESC:
                if (type == String.class) {
                    throw new ClassCastException("Cannot cast a class to java.lang.String");
                }
                return readClassDesc(unshared);

            case TC_STRING:
            case TC_LONGSTRING:
                return checkResolve(readString(unshared));

            case TC_ARRAY:
                if (type == String.class) {
                    throw new ClassCastException("Cannot cast an array to java.lang.String");
                }
                return checkResolve(readArray(unshared));

            case TC_ENUM:
                if (type == String.class) {
                    throw new ClassCastException("Cannot cast an enum to java.lang.String");
                }
                return checkResolve(readEnum(unshared));

            case TC_OBJECT:
                if (type == String.class) {
                    throw new ClassCastException("Cannot cast an object to java.lang.String");
                }
                return checkResolve(readOrdinaryObject(unshared));

            case TC_EXCEPTION:
                if (type == String.class) {
                    throw new ClassCastException("Cannot cast an exception to java.lang.String");
                }
                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);
    }
}

serialVersionUID

serialVersionUID 是用于版本控制的序列化版本號。它是一個長整型數(shù)值,用于標(biāo)識類的版本。通過顯式聲明 serialVersionUID,可以在類結(jié)構(gòu)發(fā)生變化時(shí)依然能夠正確地進(jìn)行反序列化。

@Data
public class LoginUserInfo implements Serializable {

    private static final long serialVersionUID = 1L;
    
    ...
}

如果在類中沒有明確聲明 serialVersionUID,Java 運(yùn)行時(shí)系統(tǒng)會根據(jù)類的結(jié)構(gòu)自動生成一個。這種自動生成的 serialVersionUID 是基于類的各個方面的,包括字段、方法、父類等。如果類的結(jié)構(gòu)發(fā)生變化,可能導(dǎo)致自動生成的 serialVersionUID 發(fā)生變化。這可能會導(dǎo)致在反序列化時(shí),類的版本不一致,從而導(dǎo)致 InvalidClassException 異常。

所以顯式聲明 serialVersionUID是確保反序列化過程正確的關(guān)鍵,避免因類結(jié)構(gòu)變化而導(dǎo)致的問題。

transient 關(guān)鍵字

關(guān)鍵字 transient 用于標(biāo)記字段,表示在對象序列化的過程中,這個字段應(yīng)該被忽略。例如,如果一個類有一個不希望被序列化的緩存字段,可以使用 transient 關(guān)鍵字來避免將其寫入序列化數(shù)據(jù)。例如ArrayList、LinkedList 等類中的一些屬性就是使用transient修飾的:

_20231218215928.jpg

_20231218215951.jpg

自定義序列化和反序列化

有時(shí)候,可能需要自定義序列化和反序列化的過程以滿足特定需求??梢酝ㄟ^實(shí)現(xiàn) writeObject 和 readObject 方法來實(shí)現(xiàn)自定義邏輯。如ArrayList類中就是通過自定義的序列化和反序列化方法:

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

/**
 * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
 * deserialize it).
 */
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        int capacity = calculateCapacity(elementData, size);
        SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}

序列化不保存靜態(tài)變量

  • 對象狀態(tài) vs. 類狀態(tài)

序列化的主要目的是保存對象的狀態(tài),即對象的實(shí)例變量。靜態(tài)變量是類級別的,它們對于每個對象實(shí)例都是相同的。序列化關(guān)注的是對象的實(shí)例狀態(tài),因?yàn)檫@是對象在不同環(huán)境中重建時(shí)所需的關(guān)鍵信息。

  • 節(jié)省空間

靜態(tài)變量通常用于表示類級別的常量或共享數(shù)據(jù),這些數(shù)據(jù)在所有對象實(shí)例之間是相同的。如果每個對象的靜態(tài)變量都被序列化并存儲,將導(dǎo)致冗余,浪費(fèi)存儲空間。序列化的目標(biāo)之一是盡可能緊湊地保存對象的狀態(tài),因此不保存靜態(tài)變量是一種優(yōu)化。

  • 不需要還原

靜態(tài)變量在類加載時(shí)初始化,并在整個應(yīng)用程序的生命周期內(nèi)保持不變。因此,在反序列化時(shí)不需要重新初始化靜態(tài)變量。序列化和反序列化的目標(biāo)是保存和還原對象的動態(tài)狀態(tài),而不是類級別的靜態(tài)狀態(tài)。

序列化的安全性和性能考慮

在實(shí)際應(yīng)用中,需要注意序列化的安全性和性能。反序列化過程中可能存在安全風(fēng)險(xiǎn),因此要謹(jǐn)慎處理來自不受信任源的序列化數(shù)據(jù)。此外,對于大量數(shù)據(jù)的序列化,可能會影響系統(tǒng)性能,可以考慮使用更高效的序列化工具或壓縮算法。

總結(jié)

綜合來看,Java 序列化的核心思想是將對象的狀態(tài)轉(zhuǎn)換為字節(jié)流,并通過 ObjectOutputStream 類完成這一過程。該類在內(nèi)部處理了對象引用的記錄、對象字段的寫入、自定義寫入方法的執(zhí)行等。在實(shí)際應(yīng)用中,我們需要注意序列化版本控制、對象字段的 transient 關(guān)鍵字的處理以及序列化性能等方面的問題。

請注意,Java 序列化機(jī)制在現(xiàn)代應(yīng)用中可能會遇到一些挑戰(zhàn),包括性能問題、安全性問題以及與其他語言的兼容性等。因此,在一些場景下,開發(fā)者可能會考慮使用其他序列化框架,如 JSON 或 Protocol Buffers,以滿足不同的需求。

以上就是Java序列化機(jī)制詳解的詳細(xì)內(nèi)容,更多關(guān)于Java序列化機(jī)制的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot利用自定義注解實(shí)現(xiàn)隱私數(shù)據(jù)脫敏(加密顯示)的解決方案

    SpringBoot利用自定義注解實(shí)現(xiàn)隱私數(shù)據(jù)脫敏(加密顯示)的解決方案

    這兩天在整改等保測出的問題,里面有一個“用戶信息泄露”的風(fēng)險(xiǎn)項(xiàng)(就是后臺系統(tǒng)里用戶的一些隱私數(shù)據(jù)直接明文顯示了),其實(shí)指的就是要做數(shù)據(jù)脫敏,本文給大家介紹了SpringBoot利用自定義注解實(shí)現(xiàn)隱私數(shù)據(jù)脫敏(加密顯示)的解決方案,需要的朋友可以參考下
    2023-11-11
  • java反射實(shí)現(xiàn)javabean轉(zhuǎn)json實(shí)例代碼

    java反射實(shí)現(xiàn)javabean轉(zhuǎn)json實(shí)例代碼

    基于java反射機(jī)制實(shí)現(xiàn)javabean轉(zhuǎn)json字符串實(shí)例,大家參考使用吧
    2013-12-12
  • java實(shí)現(xiàn)三角形分形山脈

    java實(shí)現(xiàn)三角形分形山脈

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)三角形分形山脈,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • Mybatis-plus支持Gbase8s分頁的實(shí)現(xiàn)示例

    Mybatis-plus支持Gbase8s分頁的實(shí)現(xiàn)示例

    本文主要介紹了Mybatis-plus支持Gbase8s分頁的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Spring Security使用權(quán)限注解實(shí)現(xiàn)精確控制

    Spring Security使用權(quán)限注解實(shí)現(xiàn)精確控制

    在現(xiàn)代的應(yīng)用系統(tǒng)中,權(quán)限管理是確保系統(tǒng)安全性的重要環(huán)節(jié),Spring Security作為Java世界最為普及的安全框架,提供了強(qiáng)大而靈活的權(quán)限控制功能,這篇文章將深入探討Spring Security使用權(quán)限注解實(shí)現(xiàn)精確控制,需要的朋友可以參考下
    2024-12-12
  • Spring?Framework六種常見設(shè)計(jì)模式

    Spring?Framework六種常見設(shè)計(jì)模式

    設(shè)計(jì)模式是軟件開發(fā)的重要組成部分,本文借助spring來講解這個框架的設(shè)計(jì)模式,通過本文我們探討了spring如何利用這些模式來提供這些豐富的功能,對本文感興趣的朋友跟隨小編一起看看吧
    2023-06-06
  • java中不同版本JSONObject區(qū)別小結(jié)

    java中不同版本JSONObject區(qū)別小結(jié)

    本文主要介紹了java中不同版本JSONObject區(qū)別小結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-02-02
  • java實(shí)現(xiàn)人工智能化屏幕監(jiān)控窗口

    java實(shí)現(xiàn)人工智能化屏幕監(jiān)控窗口

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)人工智能化屏幕監(jiān)控窗口,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • 最最常用的 100 個 Java類分享

    最最常用的 100 個 Java類分享

    這篇文章主要介紹了最最常用的 100 個 Java類分享,需要的朋友可以參考下
    2015-04-04
  • Spring異常捕獲且回滾事務(wù)解決方案

    Spring異常捕獲且回滾事務(wù)解決方案

    這篇文章主要介紹了Spring異常捕獲且回滾事務(wù)解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06

最新評論