在Java中如何使用transient

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

transient的用途

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

A:當對象被序列化時(寫入字節序列到目標文件)時,transient阻止實例中那些用此關鍵字聲明的變量持久化;當對象被反序列化時(從源文件讀取字節序列進行重構),這樣的實例變量值不會被持久化和恢復。例如,當反序列化對象——數據流(例如,文件)可能不存在時,原因是你的對象中存在類型爲java.io.InputStream的變量,序列化時這些變量引用的輸入流無法被打開。

transient使用介紹

Q:如何使用transient?

A:包含實例變量聲明中的transient修飾符。片段1提供了小的演示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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。你或許會看到類似下面的輸出:

1
2
3
4
5
6
7
8
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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)。你可以看到如下輸出:

1
2
3
4
5
6
7
8
9
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十六進制:

1
2
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不能被序列化,原因是它們類變量不能序列化。

原文鏈接: javaworld 翻譯: ImportNew.com xbing
譯文鏈接: http://www.importnew.com/12611.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章