第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

全部開發(fā)者教程

Java 序列化與反序列化

上一小節(jié)我們學(xué)習(xí)了 Java 的輸入輸出流,有了這些前置知識(shí)點(diǎn),我們就可以學(xué)習(xí) Java 的序列化了。本小節(jié)將介紹什么是序列化、什么是反序列化、序列化有什么作用,如何實(shí)現(xiàn)序列化與反序列化,Serializable 接口介紹,常用序列化工具介紹等內(nèi)容。了解序列化的用途、學(xué)會(huì)如何進(jìn)行序列化和反序列化操作是本小節(jié)的重點(diǎn)內(nèi)容。

1. 序列化與反序列化

序列化在計(jì)算機(jī)科學(xué)的數(shù)據(jù)處理中,是指將數(shù)據(jù)結(jié)構(gòu)或?qū)ο鬆顟B(tài)轉(zhuǎn)換成可取用格式,以留待后續(xù)在相同或另一臺(tái)計(jì)算機(jī)環(huán)境中,能恢復(fù)原先狀態(tài)的過程。依照序列化格式重新獲取字節(jié)的結(jié)果時(shí),可以利用它來(lái)產(chǎn)生與原始對(duì)象相同語(yǔ)義的副本。

很多編程語(yǔ)言自身就支持序列化操作。Java 語(yǔ)言提供自動(dòng)序列化,序列化(serialize)就是將對(duì)象轉(zhuǎn)換為字節(jié)流;與之相應(yīng)對(duì)的,反序列化(deserialize)就是將字節(jié)流轉(zhuǎn)換為對(duì)象。

需要注意的是,Java 序列化對(duì)象時(shí),會(huì)把對(duì)象的狀態(tài)保存成字節(jié)序列,對(duì)象的狀態(tài)指的就是其成員變量,因此序列化的對(duì)象不會(huì)保存類的靜態(tài)變量。

在 Java 中,可通過對(duì)象輸出/輸入流來(lái)實(shí)現(xiàn)序列化/反序列化操作。 java.io包中,提供了ObjectInputStream類和ObjectOutputStream用來(lái)序列化對(duì)象,這兩個(gè)類我們將在下面介紹。下面我們來(lái)介紹一下序列化的作用。

2. 序列化的作用

  • 序列化可以將對(duì)象的字節(jié)序列存持久化:可以將其保存在內(nèi)存、文件、數(shù)據(jù)庫(kù)中(見下圖);
  • 可以在網(wǎng)絡(luò)上傳輸對(duì)象字節(jié)序列;
  • 可用于遠(yuǎn)端程序方法調(diào)用。

3. 實(shí)現(xiàn)序列化

  • ObjectOutputStream類下的void writeObject(Object obj)方法用于將一個(gè)對(duì)象寫入對(duì)象輸出流,也就是序列化;
  • ObjectInputStream類下的Object readObject()方法用于讀取一個(gè)對(duì)象到輸入流,也就是反序列化。

實(shí)例代碼如下:

import java.io.*;

public class SerializeDemo1 {

    static class Cat implements Serializable {
        private static final long serialVersionUID = 1L;

        private String nickname;

        private Integer age;

        public Cat() {}

        public Cat(String nickname, Integer age) {
            this.nickname = nickname;
            this.age = age;
        }

        @Override
        public String toString() {
            return "Cat{" +
                    "nickname='" + nickname + '\'' +
                    ", age=" + age +
                    '}';
        }
    }

    /**
     * 序列化方法
     * @param filepath 文件路徑
     * @param cat 要序列化的對(duì)象
     * @throws IOException
     */
    private static void serialize(String filepath, Cat cat) throws IOException {
        // 實(shí)例化file對(duì)象
        File file = new File(filepath);
        // 實(shí)例化文件輸出流
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        // 實(shí)例化對(duì)象輸出流
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        // 保存cat對(duì)象
        objectOutputStream.writeObject(cat);
        // 關(guān)閉流
        fileOutputStream.close();
        objectOutputStream.close();
    }

    /**
     * 反序列化方法
     * @param filepath 文件路徑
     * @throws IOException
     * @throws ClassNotFoundException
     */
    private static void deserialize(String filepath) throws IOException, ClassNotFoundException {
        // 實(shí)例化file對(duì)象
        File file = new File(filepath);
        // 實(shí)例化文件輸入流
        FileInputStream fileInputStream = new FileInputStream(file);
        // 實(shí)例化對(duì)象輸入流
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        Object o = objectInputStream.readObject();
        System.out.println(o);
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        String filename = "C:\\Users\\Colorful\\Desktop\\imooc\\Hello.txt";
        Cat cat = new Cat("豬皮", 1);
        serialize(filename, cat);
        deserialize(filename);
    }

}

運(yùn)行結(jié)果:

Cat{nickname='豬皮', age=1}

上述代碼中,我們定義了一個(gè)Cat類,它實(shí)現(xiàn)了Serializable接口,類內(nèi)部有一個(gè)private static final long serialVersionUID = 1L;,關(guān)于這兩點(diǎn),我們下面緊接著就會(huì)介紹。

除了Cat類的定義,我們還分別封裝了序列化與反序列化的方法,并在主方法中調(diào)用了這兩個(gè)方法,實(shí)現(xiàn)了cat對(duì)象的序列化和反序列化操作。

在調(diào)用序列化方法后,你會(huì)發(fā)現(xiàn)磁盤中的Hello.txt文件中被cat對(duì)象寫入了序列化后的數(shù)據(jù):

4. Seralizable 接口

被序列化的類必須是EnumArraySerializable中的任意一種類型。

如果要序列化的類不是枚舉類型和數(shù)組類型的話,則必須實(shí)現(xiàn)java.io.Seralizable接口,否則直接序列化將拋出NotSerializableException異常。

4.1 serialVersionUID

serialVersionUID 是 Java 為每個(gè)序列化類產(chǎn)生的版本標(biāo)識(shí)。它可以用來(lái)保證在反序列化時(shí),發(fā)送方發(fā)送的和接受方接收的是可兼容的對(duì)象。如果接收方接收的類的 serialVersionUID 與發(fā)送方發(fā)送的 serialVersionUID 不一致,會(huì)拋出 InvalidClassException

4.2 默認(rèn)序列化機(jī)制

如果僅僅只是讓某個(gè)類實(shí)現(xiàn) Serializable 接口,而沒有其它任何處理的話,那么就會(huì)使用默認(rèn)序列化機(jī)制。

使用默認(rèn)機(jī)制,在序列化對(duì)象時(shí),不僅會(huì)序列化當(dāng)前對(duì)象本身,還會(huì)對(duì)其父類的字段以及該對(duì)象引用的其它對(duì)象也進(jìn)行序列化。同樣地,這些其它對(duì)象引用的另外對(duì)象也將被序列化,以此類推。所以,如果一個(gè)對(duì)象包含的成員變量是容器類對(duì)象,而這些容器所含有的元素也是容器類對(duì)象,那么這個(gè)序列化的過程就會(huì)較復(fù)雜,開銷也較大。

4.3 transient 關(guān)鍵字

在現(xiàn)實(shí)應(yīng)用中,有些時(shí)候不能使用默認(rèn)序列化機(jī)制。比如,希望在序列化過程中忽略掉敏感數(shù)據(jù),或者簡(jiǎn)化序列化過程。下面將介紹若干影響序列化的方法。

當(dāng)某個(gè)字段被聲明為 transient 后,默認(rèn)序列化機(jī)制就會(huì)忽略該字段。

可以嘗試將實(shí)例代碼中Cat類的成員變量age聲明為transient

// 僅部分代碼
static class Cat implements Serializable {
    transient private Integer age;
}

運(yùn)行程序,我們會(huì)發(fā)現(xiàn)成員變量age沒有被序列化。

5. 常用序列化工具

Java 官方的序列化存在很多缺點(diǎn),因此,開發(fā)者們更傾向于使用優(yōu)秀的第三方序列化工具來(lái)替代 Java 自身的序列化機(jī)制。

Java 官方的序列化主要體現(xiàn)在以下方面:

  • 性能問題:序列化后的數(shù)據(jù)相對(duì)于一些優(yōu)秀的序列化的工具,還是要大不少,這大大影響存儲(chǔ)和傳輸?shù)男剩?/li>
  • 繁瑣的步驟:Java 官方的序列化一定需要實(shí)現(xiàn) Serializable 接口,略顯繁瑣,而且需要關(guān)注 serialVersionUID;
  • 無(wú)法跨語(yǔ)言使用:序列化的很大一個(gè)目的就是用于不同語(yǔ)言來(lái)讀寫數(shù)據(jù)。

下面列舉了一些優(yōu)秀的序列化工具:

  • thrift、protobuf - 適用于對(duì)性能敏感,對(duì)開發(fā)體驗(yàn)要求不高的內(nèi)部系統(tǒng)。
  • hessian - 適用于對(duì)開發(fā)體驗(yàn)敏感,性能有要求的內(nèi)外部系統(tǒng)。
  • jackson、gsonfastjson - 適用于對(duì)序列化后的數(shù)據(jù)要求有良好的可讀性(轉(zhuǎn)為 json 、xml 形式)。

6. 小結(jié)

通過本小節(jié)的學(xué)習(xí),我們知道了序列化(serialize)就是將對(duì)象轉(zhuǎn)換為字節(jié)流,反序列化(deserialize)就是將字節(jié)流轉(zhuǎn)換為對(duì)象。想要實(shí)現(xiàn)序列化,就必須繼承Seralizable接口,serialVersionUID 是 Java 為每個(gè)序列化類產(chǎn)生的版本標(biāo)識(shí)。當(dāng)某個(gè)字段被聲明為 transient 后,默認(rèn)序列化機(jī)制就會(huì)忽略該字段。學(xué)會(huì)根據(jù)自己的應(yīng)用場(chǎng)景選擇使用序列化工具。