亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Java ---- 序列化

zorpan / 3292人閱讀

摘要:使用對(duì)象序列化,在保存對(duì)象時(shí),會(huì)把其狀態(tài)保存為一組字節(jié),在未來,再將這些字節(jié)組裝成對(duì)象。由此可知,對(duì)象序列化不會(huì)關(guān)注類中的靜態(tài)變量。對(duì)象的讀寫類中對(duì)象的序列化工作是通過和來完成的。這就是為什么在此序列化過程中的無參構(gòu)造器會(huì)被調(diào)用。

Java對(duì)象的序列化

Java平臺(tái)允許我們?cè)趦?nèi)存中創(chuàng)建可復(fù)用的Java對(duì)象,但一般情況下,只有當(dāng)JVM處于運(yùn)行時(shí),這些對(duì)象才可能存在,即,這些對(duì)象的生命周期不會(huì)比JVM的生命周期更長。但在現(xiàn)實(shí)應(yīng)用中,就可能要求在JVM停止運(yùn)行之后能夠保存(持久化)指定的對(duì)象,并在將來重新讀取被保存的對(duì)象。Java對(duì)象序列化就能夠幫助我們實(shí)現(xiàn)該功能。

使用Java對(duì)象序列化,在保存對(duì)象時(shí),會(huì)把其狀態(tài)保存為一組字節(jié),在未來,再將這些字節(jié)組裝成對(duì)象。必須注意地是,對(duì)象序列化保存的是對(duì)象的”狀態(tài)”,即它的成員變量。由此可知,對(duì)象序列化不會(huì)關(guān)注類中的靜態(tài)變量。

簡而言之,就是讓對(duì)象想基本數(shù)據(jù)類型和字符串類型一樣,通過輸入輸出字節(jié)流ObjectInputStream 和 ObjectOutputStream進(jìn)行寫和讀操作。

Java序列化的應(yīng)用場景

當(dāng)你想把的內(nèi)存中的對(duì)象狀態(tài)保存到一個(gè)文件中或者數(shù)據(jù)庫中時(shí)候

當(dāng)你想用套接字在網(wǎng)絡(luò)上傳送對(duì)象的時(shí)候

當(dāng)你想通過RMI傳輸對(duì)象的時(shí)候

如何對(duì)Java對(duì)象進(jìn)行序列化與反序列化

在Java中,只要一個(gè)類實(shí)現(xiàn)了java.io.Serializable接口,那么它就可以被序列化。

import java.io.*;
import java.util.*;

class User implements Serializable {
    private String name;
    private int age;
    private Date birthday;
    private transient String gender;
    private static int test =1;
    private static final long serialVersionUID = -6849794470754667710L;

    public User() {
        System.out.println("none-arg constructor");
    }

    public User(String name, Integer age,Date birthday,String gender) {
        System.out.println("arg constructor");
        this.name = name;
        this.age = age;
        this.birthday = birthday;
        this.gender = gender;
    }

    public void setTest(int Newtest) {
        this.test = Newtest;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "User{" +
                "name="" + name + """ +
                ", age=" + age +
                ", gender=" + gender +
                ", birthday=" + birthday +
                ", testStatic="+test+
                "}"+" "+super.toString();
    }
}

public class SerializableDemo {
    public static void main(String[] args) throws Exception {
        //Initializes The Object
        User user = new User("qiuyu",23,new Date(),"male");
        System.out.println(user);
        user.setTest(10);
        System.out.println(user);

        //Write Obj to File
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tempFile"));
        out.writeObject(user);
        out.close();

        //Read Obj from File
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("tempFile"));
        User newUser = (User) in.readObject();
        System.out.println(newUser);
        in.close();
    }
}

此時(shí)的輸出為:

arg constructor
User{name="qiuyu", age=23, gender=male, birthday=Tue Nov 14 20:38:57 GMT+08:00 2017, testStatic=10} User@326de728
User{name="qiuyu", age=23, gender=null, birthday=Tue Nov 14 20:38:57 GMT+08:00 2017, testStatic=10} User@4883b407

此時(shí)必須注意的是,當(dāng)重新讀取被保存的User對(duì)象時(shí),并沒有調(diào)用User的任何構(gòu)造器,看起來就像是直接使用字節(jié)將User對(duì)象還原出來的。

當(dāng)User對(duì)象被保存到tempfile文件中之后,我們可以在其它地方去讀取該文件以還原對(duì)象,但必須確保該讀取程序的CLASSPATH中包含有User.class,否則會(huì)拋出ClassNotFoundException。

Q:之前不是說序列化不保存靜態(tài)變量么,為什么這里的靜態(tài)變量進(jìn)行了傳遞,都變成了10呢?
A:因?yàn)榇藭r(shí)User.class已經(jīng)被加載進(jìn)了內(nèi)存中,且將static變量test從1更改為了10。當(dāng)我們恢復(fù)對(duì)象時(shí),會(huì)直接獲取當(dāng)前static的變量test的值,所以為10。

Q:但如果我們的恢復(fù)操作在另一個(gè)文件中進(jìn)行,結(jié)果會(huì)怎么樣呢?

import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class Recovering {
    public static void main(String[] args) throws Exception{
        //Read Obj from File
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("tempFile"));
        User newUser = (User) in.readObject();
        System.out.println(newUser);
        in.close();
    }
}

輸出結(jié)果為:

User{name="qiuyu", age=23, gender=null, birthday=Tue Nov 14 20:38:57 GMT+08:00 2017, testStatic=1} User@6442b0a6

A:因?yàn)樵谶\(yùn)行此代碼時(shí),User.class會(huì)被加載進(jìn)內(nèi)存,然后執(zhí)行初始化,將被初始化為1,因此test變量會(huì)被恢復(fù)為1。注意區(qū)分上面兩種情況,不要因?yàn)槭窃诒镜剡M(jìn)行測(cè)試,就認(rèn)為static會(huì)被序列化,同時(shí)要了解Java運(yùn)行時(shí),內(nèi)存加載的機(jī)制。

基本知識(shí)點(diǎn)

Serializable接口

對(duì)于任何需要被序列化的對(duì)象,都必須要實(shí)現(xiàn)接口Serializable,它只是一個(gè)標(biāo)識(shí)接口,本身沒有任何成員,只是用來標(biāo)識(shí)說明當(dāng)前的實(shí)現(xiàn)類的對(duì)象可以被序列化.

如果父類實(shí)現(xiàn)序列化,子類自動(dòng)實(shí)現(xiàn)序列化,不需要顯式實(shí)現(xiàn)Serializable接口。

如果被寫對(duì)象的類型是String,數(shù)組,Enum,Serializable,那么就可以對(duì)該對(duì)象進(jìn)行序列化,否則將拋出NotSerializableException。

對(duì)象的讀寫

Java類中對(duì)象的序列化工作是通過 ObjectOutputStream ObjectInputStream 來完成的。

使用readObject()、writeObject()方法對(duì)對(duì)象進(jìn)行讀寫操作;

對(duì)于基本類型,可以使用readInt()、writeInt() , readDouble()writeDouble()等類似的接口進(jìn)行讀寫。

序列化機(jī)制

如果僅僅只是讓某個(gè)類實(shí)現(xiàn)Serializable接口,而沒有其它任何處理的話,則就是使用默認(rèn)序列化機(jī)制。使用默認(rèn)機(jī)制,在序列化對(duì)象時(shí),不僅會(huì)序列化當(dāng)前對(duì)象本身,還會(huì)對(duì)該對(duì)象引用的其它對(duì)象也進(jìn)行序列化,同樣地,這些其它對(duì)象引用的另外對(duì)象也將被序列化。

在現(xiàn)實(shí)應(yīng)用中,有些時(shí)候不能使用默認(rèn)序列化機(jī)制。比如,希望在序列化過程中忽略掉敏感數(shù)據(jù),或者簡化序列化過程。為此需要為某個(gè)字段聲明為transient,那么序列化機(jī)制就會(huì)忽略被transient修飾的字段。transient的引用變量會(huì)以null返回,基本數(shù)據(jù)類型會(huì)以相應(yīng)的默認(rèn)值返回。(例如:引用類型沒有實(shí)現(xiàn)Serializable,或者動(dòng)態(tài)數(shù)據(jù)只可以在執(zhí)行時(shí)求出而不能或不必存儲(chǔ))

writeObject()與readObject()

對(duì)于上被聲明為transient的字段gender,除了將transient關(guān)鍵字去掉之外,是否還有其它方法能使它再次可被序列化?方法之一就是在User類中添加兩個(gè)方法:writeObject()與readObject(),如下所示:

private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeUTF(gender);
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        gender = in.readUTF();
    }

在writeObject()方法中會(huì)先調(diào)用ObjectOutputStream中的defaultWriteObject()方法,該方法會(huì)執(zhí)行默認(rèn)的序列化機(jī)制,此時(shí)會(huì)忽略掉gender字段。

然后再調(diào)用writeUtf()方法顯示地將gender字段寫入到ObjectOutputStream中。readObject()的作用則是針對(duì)對(duì)象的讀取,其原理與writeObject()方法相同。

Q: writeObject()與readObject()都是private方法,那么它們是如何被調(diào)用的呢?
A: 毫無疑問,是使用反射。(注意這不是繼承接口的方法,因?yàn)榻涌陬惖姆椒ǘ际莗ublic的,而這里的方法是private的)

Externalizable接口

無論是使用transient關(guān)鍵字,還是使用writeObject()和readObject()方法,其實(shí)都是基于Serializable接口的序列化。JDK中提供了另一個(gè)序列化接口Externalizable,使用該接口之后,之前基于Serializable接口的序列化機(jī)制就將失效。

import java.io.*;
import java.util.*;

class UserExtern implements Externalizable {
    private String name;
    private int age;
    private Date birthday;
    private transient String gender;
    private static int test =1;
    private static final long serialVersionUID = -6849794470754667710L;

    public UserExtern() {
        System.out.println("none-arg constructor");
    }

    public UserExtern(String name, Integer age,Date birthday,String gender) {
        System.out.println("arg constructor");
        this.name = name;
        this.age = age;
        this.birthday = birthday;
        this.gender = gender;
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeUTF(gender);
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        gender = in.readUTF();
    }


    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeUTF(name);
        out.writeInt(age);
        out.writeObject(birthday);
        out.writeUTF(gender);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = in.readUTF();
        age = in.readInt();
        birthday = (Date) in.readObject();
        gender = in.readUTF();
    }


    public void setTest(int Newtest) {
        this.test = Newtest;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "User{" +
                "name="" + name + """ +
                ", age=" + age +
                ", gender=" + gender +
                ", birthday=" + birthday +
                ", testStatic="+test+
                "}"+" "+super.toString();
    }
}



public class ExternalizableDemo {
    public static void main(String[] args) throws Exception {
        UserExtern userExtern = new UserExtern("qiuyu",23,new Date(),"male");
        System.out.println(userExtern);
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("external"));
        out.writeObject(userExtern);
        out.close();


        ObjectInputStream in = new ObjectInputStream(new FileInputStream("external"));
        UserExtern userExtern1 = (UserExtern)in.readObject();
        System.out.println(userExtern1)
    }
}

輸出結(jié)果為:

arg constructor
User{name="qiuyu", age=23, gender=male, birthday=Tue Nov 14 22:34:25 GMT+08:00 2017, testStatic=1} UserExtern@25618e91
none-arg constructor
User{name="qiuyu", age=23, gender=male, birthday=Tue Nov 14 22:34:25 GMT+08:00 2017, testStatic=1} UserExtern@604ed9f0

Externalizable繼承于Serializable,當(dāng)使用該接口時(shí),序列化的細(xì)節(jié)需要由程序員去完成writeExternal()readExternal()方法的具體細(xì)節(jié),以及哪些狀態(tài)需要進(jìn)行序列化。

另外,若使用Externalizable進(jìn)行序列化,當(dāng)讀取對(duì)象時(shí),會(huì)調(diào)用被序列化類的無參構(gòu)造器去創(chuàng)建一個(gè)新的對(duì)象,然后再將被保存對(duì)象的字段的值分別填充到新對(duì)象中。這就是為什么在此序列化過程中UserExtern的無參構(gòu)造器會(huì)被調(diào)用。由于這個(gè)原因,實(shí)現(xiàn)Externalizable接口的類必須要提供一個(gè)無參的構(gòu)造器,且它的訪問權(quán)限為public。

注意事項(xiàng)

讀取對(duì)象的順序必須與寫入的順序相同

如果有不能被序列化的對(duì)象,執(zhí)行期間就會(huì)拋出NotSerializableException異常

序列化時(shí),只對(duì)對(duì)象的狀態(tài)進(jìn)行保存,而不管對(duì)象的方法

靜態(tài)變量不會(huì)被序列化,因?yàn)樗械膶?duì)象共享同一份靜態(tài)變量的值

如果一個(gè)對(duì)象的成員變量是一個(gè)對(duì)象,那么這個(gè)對(duì)象的數(shù)據(jù)成員也會(huì)被保存還原,而且會(huì)是遞歸的方式(對(duì)象網(wǎng))。(序列化程序會(huì)將對(duì)象版圖上的所有東西儲(chǔ)存下來,這樣才能讓該對(duì)象恢復(fù)到原來的狀態(tài))

如果子類實(shí)現(xiàn)Serializable接口而父類未實(shí)現(xiàn)時(shí),父類不會(huì)被序列化,但此時(shí)父類必須有個(gè)無參構(gòu)造方法,否則會(huì)拋InvalidClassException異常;因?yàn)榉葱蛄谢瘯r(shí)會(huì)恢復(fù)原有子對(duì)象的狀態(tài),而父類的成員變量也是原有子對(duì)象的一部分。由于父類沒有實(shí)現(xiàn)序列化接口,即使沒有顯示調(diào)用,也會(huì)默認(rèn)執(zhí)行父類的無參構(gòu)造函數(shù)使變量初始化;

深入理解

序列化ID的問題

serialVersionUID適用于JAVA的序列化機(jī)制。簡單來說,Java的序列化機(jī)制是通過判斷類的serialVersionUID來驗(yàn)證版本一致性的。

在進(jìn)行反序列化時(shí),JVM會(huì)把傳來的字節(jié)流中的serialVersionUID與本地相應(yīng)實(shí)體類的serialVersionUID進(jìn)行比較,如果相同就認(rèn)為是一致的,可以進(jìn)行反序列化,否則就會(huì)出現(xiàn)序列化版本不一致的異常,即是InvalidCastException。

序列化存儲(chǔ)規(guī)則

Java 序列化機(jī)制為了節(jié)省磁盤空間,具有特定的存儲(chǔ)規(guī)則,當(dāng)寫入文件的為同一對(duì)象時(shí),并不會(huì)再將對(duì)象的內(nèi)容進(jìn)行存儲(chǔ),而只是再次存儲(chǔ)一份引用;

序列化到同一個(gè)文件時(shí),如第二次修改了相同對(duì)象屬性值再次保存時(shí)候,虛擬機(jī)根據(jù)引用關(guān)系知道已經(jīng)有一個(gè)相同對(duì)象已經(jīng)寫入文件,因此只保存第二次寫的引用,所以讀取時(shí),都是第一次保存的對(duì)象,第二次進(jìn)行的修改將無效。

    public static void main(String[] args) throws Exception {
        //Initializes The Object
        User user = new User("qiuyu",23,new Date(),"male");
        user.setTest(10);
        System.out.println(user);


        //Write Obj to File
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tempFile"));
        out.writeObject(user);
        user.setAge(25);
        System.out.println(user);
        out.writeObject(user);
        out.close();

        //Read Obj from File
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("tempFile"));
        User newUser = (User) in.readObject();
        User newUser1 = (User) in.readObject();
        System.out.println(newUser);
        System.out.println(newUser1);
        in.close();
    }

輸出結(jié)果: 注意觀察age的值

User{name="qiuyu", age=23, gender=male, birthday=Tue Nov 14 22:47:22 GMT+08:00 2017, testStatic=10} User@326de728
User{name="qiuyu", age=25, gender=male, birthday=Tue Nov 14 22:47:22 GMT+08:00 2017, testStatic=10} User@326de728
User{name="qiuyu", age=23, gender=male, birthday=Tue Nov 14 22:47:22 GMT+08:00 2017, testStatic=10} User@4883b407
User{name="qiuyu", age=23, gender=male, birthday=Tue Nov 14 22:47:22 GMT+08:00 2017, testStatic=10} User@4883b407

多次序列化的問題

在一次的序列化的過程中,ObjectOutputStream 會(huì)在文件開始的地方寫入一個(gè) Header的信息到文件中。于是在多次序列化的過程中就會(huì)繼續(xù)在文件末尾(本次序列化的開頭)寫入 Header 的信息,這時(shí)如果進(jìn)行反序列化的對(duì)象的時(shí)候會(huì)報(bào)錯(cuò):java.io.StreamCorruptedException: invalid type code: AC

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/68007.html

相關(guān)文章

  • 小伙子,你真的搞懂 transient 關(guān)鍵字了嗎?

    摘要:由以上結(jié)果分析可知,靜態(tài)變量不能被序列化,示例讀取出來的是在內(nèi)存中存儲(chǔ)的值。關(guān)鍵字總結(jié)修飾的變量不能被序列化只作用于實(shí)現(xiàn)接口只能用來修飾普通成員變量字段不管有沒有修飾,靜態(tài)變量都不能被序列化好了,棧長花了半天時(shí)間,終于整理完了。 先解釋下什么是序列化 我們的對(duì)象并不只是存在內(nèi)存中,還需要傳輸網(wǎng)絡(luò),或者保存起來下次再加載出來用,所以需要Java序列化技術(shù)。 Java序列化技術(shù)正是將對(duì)象轉(zhuǎn)...

    curlyCheng 評(píng)論0 收藏0
  • Java 對(duì)象列化

    摘要:對(duì)象序列化對(duì)象序列化機(jī)制允許把內(nèi)存中的對(duì)象轉(zhuǎn)換成與平臺(tái)無關(guān)的二進(jìn)制流,從而可以保存到磁盤或者進(jìn)行網(wǎng)絡(luò)傳輸,其它程序獲得這個(gè)二進(jìn)制流后可以將其恢復(fù)成原來的對(duì)象。 對(duì)象序列化 對(duì)象序列化機(jī)制允許把內(nèi)存中的Java對(duì)象轉(zhuǎn)換成與平臺(tái)無關(guān)的二進(jìn)制流,從而可以保存到磁盤或者進(jìn)行網(wǎng)絡(luò)傳輸,其它程序獲得這個(gè)二進(jìn)制流后可以將其恢復(fù)成原來的Java對(duì)象。 序列化機(jī)制可以使對(duì)象可以脫離程序的運(yùn)行而對(duì)立存在 ...

    tianyu 評(píng)論0 收藏0
  • Java開發(fā)中對(duì)象的列化與反列化

    摘要:在中,對(duì)象的序列化與反序列化被廣泛應(yīng)用到遠(yuǎn)程方法調(diào)用及網(wǎng)絡(luò)傳輸中。相關(guān)接口及類為了方便開發(fā)人員將對(duì)象進(jìn)行序列化及反序列化提供了一套方便的來支持。未實(shí)現(xiàn)此接口的類將無法使其任何狀態(tài)序列化或反序列化。 序列化與反序列化 序列化 (Serialization)是將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^程。一般將一個(gè)對(duì)象存儲(chǔ)至一個(gè)儲(chǔ)存媒介,例如檔案或是記億體緩沖等。在網(wǎng)絡(luò)傳輸過程中,可以...

    fox_soyoung 評(píng)論0 收藏0
  • Java列化

    摘要:的序列化是將一個(gè)對(duì)象表示成字節(jié)序列,該字節(jié)序列包括了對(duì)象的數(shù)據(jù),有關(guān)對(duì)象的類型信息和存儲(chǔ)在對(duì)象中的數(shù)據(jù)類型。任何實(shí)現(xiàn)了接口的類都可以被序列化。一旦對(duì)象被序列化或者重新裝配,就會(huì)分別調(diào)用那兩個(gè)方法。 Java序列化 1. 什么是序列化? 序列化是將一個(gè)對(duì)象的狀態(tài),各屬性的值序列化保存起來,然后在合適的時(shí)候通過反序列化獲得。 Java的序列化是將一個(gè)對(duì)象表示成字節(jié)序列,該字節(jié)序列包括了對(duì)象...

    lbool 評(píng)論0 收藏0
  • 淺談Java列化

    摘要:的序列化是將一個(gè)對(duì)象表示成字節(jié)序列,該字節(jié)序列包括了對(duì)象的數(shù)據(jù),有關(guān)對(duì)象的類型信息和存儲(chǔ)在對(duì)象中的數(shù)據(jù)類型。這個(gè)是根據(jù)類名接口名成員方法及屬性等來生成一個(gè)位的哈希字段,因?yàn)樵黾恿俗侄?,因此生成的不一樣了? Java序列化 什么是序列化? 序列化是將一個(gè)對(duì)象的狀態(tài),各屬性的值序列化保存起來,然后在合適的時(shí)候通過反序列化獲得。 Java的序列化是將一個(gè)對(duì)象表示成字節(jié)序列,該字節(jié)序列包括了對(duì)...

    winterdawn 評(píng)論0 收藏0
  • java列化和反列化說起

    摘要:從的序列化和反序列化說起序列化是將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^程,而相反的過程就稱為反序列化。當(dāng)使用接口來進(jìn)行序列化與反序列化的時(shí)候需要開發(fā)人員重寫與方法。 從java的序列化和反序列化說起 序列化 (Serialization)是將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^程,而相反的過程就稱為反序列化。 在java中允許我們創(chuàng)建可復(fù)用的對(duì)象,但是這些對(duì)象僅僅存在j...

    whlong 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<