首頁  >  文章  >  Java  >  在 Java 中如何使用 transient

在 Java 中如何使用 transient

黄舟
黄舟原創
2017-02-20 10:13:291435瀏覽

Java語言的transient不像class、synchronized和其他熟悉的關鍵字那麼眾所周知,因而它會出現在一些面試題中。這篇文章我將為大家講解transient。

transient的用途

Q:transient關鍵字能實現什麼?

A:當物件被序列化時(寫入位元組序列到目標檔案)時,transient阻止實例中那些用此關鍵字聲明的變數持久化;當物件被反序列化時(從原始檔讀取位元組序列進行重構),這樣的實例變數值不會被持久化和恢復。例如,當反序列化物件-資料流(例如,檔案)可能不存在時,原因是你的物件中存在類型為java.io.InputStream的變量,序列化時這些變數所引用的輸入流無法被開啟。

transient使用介紹

Q:如何使用transient?

A:包含實例變數宣告中的transient修飾符。片段1提供了小的示範。

import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io .IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class ClassLib implements Serializable {
    private transient InputStream is;
    private int majorVer;
    private int minorVer;
    ClassLib(InputStream is) throws IOException {
        System.out.println("ClassLib(InputStream) called");
        this.is = is;
        DataInputStream dis;
        if (is instanceof DataInputStream)
            dis = (DataInputStream) is;
        else
            dis = new DataInputStream(is);
        if (dis.readInt() != 0xcafebabe)
            throw new IOException("not a .class file");
        minorVer = dis.readShort();
        majorVer = dis.readShort();
    }
    int getMajorVer() {
        return majorVer;
    }
    int getMinorVer() {
        return minorVer;
    }
    void showIS() {
        System.out.println(is);
    }
}
public class TransDemo {
    public static void main(String[] args) throws IOException {
        if (args.length != 1) {
            System.err.println("usage: java TransDemo classfile");
            return;
        }
        ClassLib cl = new ClassLib(new FileInputStream(args[0]));
        System.out.printf("Minor version number: %d%n", cl.getMinorVer());
        System.out.printf("Major version number: %d%n", cl.getMajorVer());
        cl.showIS();
        try (FileOutputStream fos = new FileOutputStream("x.ser");
                ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            oos.writeObject(cl);
        }
        cl = null;
        try (FileInputStream fis = new FileInputStream("x.ser");
                ObjectInputStream ois = new ObjectInputStream(fis)) {
            System.out.println();
            cl = (ClassLib) ois.readObject();
            System.out.printf("Minor version number: %d%n", cl.getMinorVer());
            System.out.printf("Major version number: %d%n", cl.getMajorVer());
            cl.showIS();
        } catch (ClassNotFoundException cnfe) {
            System.err.println(cnfe.getMessage());
        }
    }
}

片段1 :序列化和反序列化ClassLib物件

片段1中宣告ClassLib和TransDemo類別。 ClassLib是一個讀取Java類別檔案的函式庫,並且實作了java.io.Serializable接口,從而這些實例能被序列化和反序列化。 TransDemo是一個用來序列化和反序列化ClassLib實例的應用類別。

ClassLib聲明它的實例變數為transient,原因是它可以毫無意義的序列化一個輸入流(像上面講述的那樣)。事實上,如果此變數不是transient的話,當反序列化x.ser的內容時,則會拋出java.io.NotSerializableException,原因是InputStream沒有實作Serializable介面。

編譯片段1:javac TransDemo.java;帶一個參數TransDemo.class運行應用:java TransDemo TransDemo.class。你或許會看到類似下面的輸出:

ClassLib(InputStream) called
Minor version number: 0
Major version number: 51
java.io.FileInputStream@79f1e0e0
Minor version number: 0
Major version number: 51
null

以上輸出顯示:當物件被重構時,沒有建構方法呼叫。此外,is假定預設為null,相比較,當ClassLib物件序列化時,majorVer和minorVer是有值的。

類別中的成員變數和transient

Q:類別中的成員變數中可以使用transient嗎?

A:問題答案請看片段2

public class TransDemo {
    public static void main(String[] args) throws IOException {
        Foo foo = new Foo();
        System.out.printf("w: %d%n", Foo.w);
        System.out.printf("x: %d%n", Foo.x);
        System.out.printf("y: %d%n", foo.y);
        System.out.printf("z: %d%n", foo.z);
        try (FileOutputStream fos = new FileOutputStream("x.ser");
                ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            oos.writeObject(foo);
        }
        foo = null;
        try (FileInputStream fis = new FileInputStream("x.ser");
                ObjectInputStream ois = new ObjectInputStream(fis)) {
            System.out.println();
            foo = (Foo) ois.readObject();
            System.out.printf("w: %d%n", Foo.w);
            System.out.printf("x: %d%n", Foo.x);
            System.out.printf("y: %d%n", foo.y);
            System.out.printf("z: %d%n", foo.z);
        } catch (ClassNotFoundException cnfe) {
            System.err.println(cnfe.getMessage());
        }
    }
}

片段2:序列化與反序列化Foo物件

片段2有點類似片段1。但不同的是,序列化和反序列化的是Foo對象,而不是ClassLib。此外,Foo包含一對變量,w和x,以及實例變數y和z。

編譯片段2(javac TransDemo.java)並執行應用(java TransDemo)。你可以看到如下輸出:

w: 1
x: 2
y: 3
z: 4
w: 1
x: 2
y: 3
z: 0

這個輸出告訴我們,實例變數y是被序列化的,z卻沒有,它被標記transient。但是,當Foo被序列化時,它並沒有告訴我們,是否變數w和x被序列化和反序列化,是否只是以普通類別初始化方式初始。對於答案,我們需要查看x.ser的內容。

下方顯示x.ser十六進位:

00000000 AC ED 00 05 73 72 00 03 46 6F 6F FC 7A 5D 82 1D ....sr..Foo.z]..
00000010 D2 9D 3F 02 00 01 49 00 01 79 78 70 00 00 00 03 ..?...I..yxp....

由於JavaWorld中的「The Java serialization algorithm revealed」這篇文章,我們發現輸出的意義:

AC ED 序列化協定標識

00 05 流版本號

73 表示這是一個新物件

72 表示這是一個新的類別

00 03 表示類別名稱長度(3)

46 6F 6F 表示類別名稱(Foo)

FC 7A 5D 82 1D D2 9D 3F 表示類別的序列版本標識符

02 表示該物件支援序列化

00 01 表示這個類別的變數數量(1)

49 變數類型代碼(0×49, 或I, 表示int)

00 01 表示變數名稱長度(1)

79 變數名稱(y)

78 表示該物件可選的資料區塊末端

70 表示我們已經到達類別層次結構的頂部

00 00 00 03 表示y的值(3)

顯而易見,只有實例變數y被序列化。因為z是transient,所以不能序列化。此外,即使它們標記transien,w和x不能被序列化,原因是它們類別變數不能序列化。

 以上就是在 Java 中如何使用 transient 的內容,更多相關內容請關注PHP中文網(www.php.cn)!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn