java序列化

java中的序列化

經(jīng)常聽(tīng)到關(guān)于序列化的話題,但是一直沒(méi)有理解什么是序列化,為什么要序列化。

首先百度了一下序列化的定義:序列化(Serialization)將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^(guò)程。在序列化期間,對(duì)象將其當(dāng)前狀態(tài)寫(xiě)入臨時(shí)或持久性存儲(chǔ)區(qū)。以后可以通過(guò)從存儲(chǔ)區(qū)中讀取或反序列化對(duì)象的狀態(tài),重新創(chuàng)建該對(duì)象。

在java中,存在于java虛擬機(jī)中的對(duì)象,他的內(nèi)部狀態(tài)只保存在內(nèi)存中。如果jvm停止了這些狀態(tài)就會(huì)丟失。在java中實(shí)現(xiàn)基本的對(duì)象序列化是件很簡(jiǎn)單的事。需要序列化的java類(lèi)只需要實(shí)現(xiàn)java.io.Serializable接口即可。這個(gè)接口只是作為一個(gè)標(biāo)識(shí),表示這個(gè)類(lèi)可以進(jìn)行序列化和反序列化。

序列化的特點(diǎn):如果一個(gè)類(lèi)能夠被序列化,那么他的子類(lèi)也可以被序列化。這里有個(gè)問(wèn)題就是:如果子類(lèi)需要序列化的話,還是要顯式的聲明serialVersionUID。在反序列化的時(shí)候聲明為statictransient類(lèi)型的成員變量不能被序列化。static表示類(lèi)的狀態(tài),transient表示對(duì)象的臨時(shí)數(shù)據(jù)。

序列化的運(yùn)用場(chǎng)景:
1.需要把內(nèi)存中的對(duì)象狀態(tài)保存到一個(gè)文件或者數(shù)據(jù)庫(kù)中。
2.需要使用套接字在網(wǎng)絡(luò)傳輸對(duì)象。
3.需要使用RMI傳輸對(duì)象。
java對(duì)象序列化不僅保留一個(gè)對(duì)象的數(shù)據(jù),而且遞歸保存對(duì)象引用的每個(gè)對(duì)象的數(shù)據(jù)。當(dāng)然這些屬性對(duì)象也需要實(shí)現(xiàn)接口java.io.Serializable

實(shí)際的序列化和反序列化工作是通過(guò)ObjectOutputStreamObjectInputStream來(lái)完成。ObjectOutputStreamwriteObject方法可以把一個(gè)java對(duì)象寫(xiě)到流中。ObjectInputStreamreadObject方法可以從流中讀取一個(gè)java對(duì)象。雖然用的參數(shù)或者返回值都是單個(gè)對(duì)象,但是實(shí)際操作的是一個(gè)對(duì)象圖。包括了這個(gè)對(duì)象狀態(tài)所引用的其他對(duì)象,以及具有相關(guān)關(guān)系的對(duì)象。會(huì)自動(dòng)遍歷這個(gè)對(duì)象圖逐個(gè)序列化。基本數(shù)據(jù)類(lèi)型和數(shù)組也是可以通過(guò)他們序列化的。

serialVersionUID序列化版本號(hào)。
序列化運(yùn)行時(shí)使用了serialVersionUID與每個(gè)可序列化類(lèi)相關(guān)聯(lián),該序列號(hào)在反序列化過(guò)程中用于驗(yàn)證序列化對(duì)象的發(fā)送者和接收者是否為該對(duì)象加載了與序列化兼容的類(lèi)。為了保證序列化版本號(hào)的一致性,序列化類(lèi)需要聲明一個(gè)明確的版本號(hào)值。

private static final long serialVersionUID = 1L;

serialVersionUID字段只是一個(gè)標(biāo)識(shí)值。只用于當(dāng)前這個(gè)類(lèi)。

序列化機(jī)制:序列化分為兩大部分:序列化和反序列化。序列化是這個(gè)過(guò)程的第一部分,將數(shù)據(jù)分解成字節(jié)流,以便存儲(chǔ)在文件中或在網(wǎng)絡(luò)上傳輸。反序列化就是打開(kāi)字節(jié)流并重構(gòu)對(duì)象。對(duì)象序列化不僅要將基本數(shù)據(jù)類(lèi)型轉(zhuǎn)換成字節(jié) 表示,有時(shí)還要恢復(fù)數(shù)據(jù)。恢復(fù)數(shù)據(jù)要求有恢復(fù)數(shù)據(jù)的對(duì)象實(shí)例。ObjectOutputStream中的序列化過(guò)程與字節(jié)流連接,包括對(duì)象類(lèi)型和版本信 息。反序列化時(shí),JVM用頭信息生成對(duì)象實(shí)例,然后將對(duì)象字節(jié)流中的數(shù)據(jù)復(fù)制到對(duì)象數(shù)據(jù)成員中。

真正在需要序列化的時(shí)候都會(huì)根據(jù)實(shí)現(xiàn)的接口來(lái)進(jìn)行操作。
當(dāng)然也可以自己定義序列化的方法。

public interface Externalizable extends java.io.Serializable{}

一個(gè)類(lèi)如果要完全負(fù)責(zé)自己的序列化,就可以實(shí)現(xiàn)Externalizable接口,自己實(shí)現(xiàn)里面的兩個(gè)方法。利用這些方法可以控制對(duì)象數(shù)據(jù)成員如何寫(xiě)入字節(jié)流.類(lèi)實(shí)現(xiàn) Externalizable時(shí),頭寫(xiě)入對(duì)象流中,然后類(lèi)完全負(fù)責(zé)序列化和恢復(fù)數(shù)據(jù)成員,除了頭以外,根本沒(méi)有自動(dòng)序列化。這里要注意了。聲明類(lèi)實(shí)現(xiàn) Externalizable接口會(huì)有重大的安全風(fēng)險(xiǎn)。writeExternal()與readExternal()方法聲明為public,惡意類(lèi)可 以用這些方法讀取和寫(xiě)入對(duì)象數(shù)據(jù)。如果對(duì)象包含敏感信息,則要格外小心。

public class SerializerUtils {
    /**
     * 序列化
     * @param object
     * @return
     */
    public static byte[] serialize(Object object) {
        ObjectOutputStream objectOutputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(object);
            byte[] bytes = byteArrayOutputStream.toByteArray();
            return bytes;
        } catch (Exception e) {
            return null;
        } finally {
            if (objectOutputStream != null) {
                try {
                    objectOutputStream.close();
                } catch (IOException e) {
                    //ignore
                }
            }
            if (byteArrayOutputStream != null) {
                try {
                    byteArrayOutputStream.close();
                } catch (IOException e) {
                    //ignore
                }
            }
        }
    }
    /**
     *  反序列化
     */
    @SuppressWarnings("unchecked")
    public static <T> T unserialize(byte[] bytes) {
        ByteArrayInputStream byteArrayInputStream = null;
        ObjectInputStream objectInputStream = null;
        try {
            byteArrayInputStream = new ByteArrayInputStream(bytes);
            objectInputStream = new ObjectInputStream(byteArrayInputStream);
            return (T) objectInputStream.readObject();
        } catch (Exception e) {
            return null;
        } finally {
            if (objectInputStream != null) {
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    //ignore
                }
            }
            if (byteArrayInputStream != null) {
                try {
                    byteArrayInputStream.close();
                } catch (IOException e) {
                    //ignore
                }
            }
        }
    }
}

對(duì)象序列化成byte[]byte[]轉(zhuǎn)換成對(duì)象方法。ObjectInputStreamObjectOutputStream將對(duì)象從流中取出和寫(xiě)入到流中。這里使用的是ByteArrayInputStreamByteArrayOutputStream作為容器然后?與byte[]進(jìn)行相互轉(zhuǎn)換。

在通過(guò)ObjectInputStreamreadObject方法讀取到一個(gè)對(duì)象之后,這個(gè)對(duì)象是一個(gè)新的實(shí)例,但是其構(gòu)造方法是沒(méi)有被調(diào)用的,其中的?屬性的初始化代碼也沒(méi)有被執(zhí)行。對(duì)于那些沒(méi)有被序列化的屬性,在新創(chuàng)建出來(lái)的對(duì)象中的值都是默認(rèn)的。也就是說(shuō),這個(gè)對(duì)象從某種角度上來(lái)說(shuō)是不完備的。這有可能會(huì)造成一些隱含的錯(cuò)誤。調(diào)用者并不知道對(duì)象是通過(guò)一般的new操作符來(lái)創(chuàng)建的,還是通過(guò)反序列化所得到的。解決的辦法就是在類(lèi)的readObject方法里面,再執(zhí)行所需的對(duì)象初始化邏輯。對(duì)于一般的Java類(lèi)來(lái)說(shuō),構(gòu)造方法中包含了初始化的邏輯??梢园堰@些邏輯提取到一個(gè)方法中,在readObject方法中調(diào)用此方法。
如果序列化對(duì)象后,這個(gè)對(duì)象的類(lèi)發(fā)生了改變,這時(shí)需要注意是否需要有兼容性。一般來(lái)說(shuō),在新的版本中添加?xùn)|西不會(huì)產(chǎn)生什么問(wèn)題,而去掉一些屬性則是不行的。

java序列化與反序列化-ImportNew

Java對(duì)象序列化與RMI-infoQ

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • JAVA序列化機(jī)制的深入研究 對(duì)象序列化的最主要的用處就是在傳遞,和保存對(duì)象(object)的時(shí)候,保證對(duì)象的完整...
    時(shí)待吾閱讀 11,177評(píng)論 0 24
  • 概念 序列化是將對(duì)象狀態(tài)轉(zhuǎn)換為可保持或傳輸?shù)母袷降倪^(guò)程。與序列化相對(duì)的是反序列化,它將流轉(zhuǎn)換為對(duì)象。 這兩個(gè)過(guò)程結(jié)...
    Moonsmile閱讀 427評(píng)論 0 0
  • 如果你只知道實(shí)現(xiàn) Serializable 接口的對(duì)象,可以序列化為本地文件。那你最好再閱讀該篇文章,文章對(duì)序列化...
    jiangmo閱讀 560評(píng)論 0 2
  • 一、序列化 java序列化提供了一個(gè)框架,用來(lái)將對(duì)象編碼成字節(jié)流,并從字節(jié)流編碼中重新構(gòu)建的對(duì)象。將對(duì)象編碼為字節(jié)...
    oneWeekOneTopic閱讀 1,970評(píng)論 0 3
  • 正如前文《Java序列化心得(一):序列化設(shè)計(jì)和默認(rèn)序列化格式的問(wèn)題》中所提到的,默認(rèn)序列化方法存在各種各樣的問(wèn)題...
    登高且賦閱讀 8,751評(píng)論 0 19

友情鏈接更多精彩內(nèi)容