在 Java 中如何使用 transient
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) calledMinor version number: 0Major version number: 51java.io.FileInputStream@79f1e0e0Minor version number: 0Major version number: 51null
以上輸出表明:當物件被重構時,沒有構造方法呼叫。此外,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: 1x: 2y: 3z: 4w: 1x: 2y: 3z: 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中的“”這篇文章,我們發現輸出的含義:
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不能被序列化,原因是它們類變數不能序列化。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2730/viewspace-2809802/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java transient 的作用及使用方法Java
- Java中transient關鍵字的作用Java
- [java]transient關鍵字Java
- transient和synchronized的使用synchronized
- Java 中的 transient 關鍵字和物件序列化Java物件
- 被遺忘的Java關鍵字:transientJava
- LiteDB在.NET中如何使用
- 在Java中this關鍵字的使用Java
- 在CSS中如何使用 when/elseCSS
- 正規表示式在Java中的使用Java
- 在Java中如何優雅地判空Java
- Misans global字型在vue中如何使用Vue
- 在Linux中,如何配置和使用Xen?Linux
- 在 Swift 5 中如何使用原始字串Swift字串
- 在專案中如何直接使用hystrix?
- @Transient關鍵字
- Java 中如何使用 SQL 查詢 TXTJavaSQL
- Java中final關鍵字如何使用?Java
- 使用 Java 在Excel中建立下拉選單JavaExcel
- Redis的安裝及在Java中的使用RedisJava
- 在Linux中如何使用at命令安排任務Linux
- 你知道在springboot中如何使用WebSocket嗎Spring BootWeb
- 在vue腳手架中如何使用EChartsVueEcharts
- Hanlp在java中文分詞中的使用介紹HanLPJava中文分詞
- Java / JavaScript在TensorFlow中的入門使用指南JavaScript
- 在Java中是如何定義和宣告介面的?Java
- 在 Mac 上使用 JavaMacJava
- WWDC 2018:在Swift中如何高效地使用集合Swift
- 在Linux中,如何使用LVM管理邏輯卷?LinuxLVM
- 在Linux中如何登出其他 SSH 使用者Linux
- 在Linux中如何禁止使用者登入Linux
- 使用shell指令碼在Linux中管理Java應用程式指令碼LinuxJava
- 在Java中,使用HttpUtils實現傳送HTTP請求JavaHTTP
- 在Mac中如何用⌘鍵拖拽非使用中的視窗?Mac
- 瞬態關鍵字transient
- transient關鍵詞的概述
- 如何學習Java? 在學習Java的過程中需要掌握哪些技能?Java
- Java中如何使用泛型實現介面中的列表集合?Java泛型