文章轉載自如下出處:作者:醬油君
原出處:非醬油藏經閣
原文連結:非醬油藏經閣www.nopassby.com

在Java序列化機制中,transient這個關鍵字非常有用,本篇文章就來帶解析一下transient關鍵字。
1、transient關鍵字的定義
定義:transient隻能用來修飾成員變量(field),被transient修飾的成員變量不參與序列化過程。
簡析:Java中的對象如果想要在網絡上傳輸或者存儲在磁盤時,就必須要序列化。Java中序列化的本質是Java對象轉換為位元組序列。但是在序列化的過程中,可以允許被序列對象中的某個成員變量不參與序列化,即該對象完成序列化之後,被transient修飾的成員變量會在位元組序列中消失。
舉例:
小美的昵稱希望被人看到,但是小美的真名不希望被人看到。
public class XiaoMei implements Serializable {
private static final long serialVersionUID = -4575083234166325540L;
private String nickName;
private transient String realName;
public XiaoMei(String nickName,String realName){
this.nickName = nickName;
this.realName = realName;
}
public String toString(){
return String.format("XiaoMei.toString(): nickName=%s,realName=%s", nickName,realName);
}
}
寫個測試代碼:
public class Test {
public static void main(String[] args){
String realName="王小美", nickName="王美美";
XiaoMei x = new XiaoMei(nickName, realName);
System.out.println("序列化前:"+x.toString());
ObjectOutputStream outStream;
ObjectInputStream inStream;
//檔案儲存在本地,把這個路徑換成自己的檔案路徑 //mac的同學把jiangyoujun換成自己的使用者名 //windows的同學前面要加D:/這樣的磁盤符号 String filePath = "/Users/jiangyoujun/Documents/test.log";
try {
outStream = new ObjectOutputStream(new FileOutputStream(filePath));
outStream.writeObject(x);
inStream = new ObjectInputStream(new FileInputStream(filePath));
XiaoMei readObject = (XiaoMei)inStream.readObject();
System.out.println("序列化後:"+readObject.toString());
} catch (IOException e) {
// TODO Auto-generated catch block e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block e.printStackTrace();
}
}
}
輸出結果如下:
序列化前:XiaoMei.toString(): nickName=王美美,realName=王小美
序列化後:XiaoMei.toString(): nickName=王美美,realName=null
可以看出,使用transient關鍵字修飾的成員變量沒有被序列化。
2、transient關鍵字設計思路和底層實作思路
毫無疑問,這是一個平常的程式設計語言設計思路,即實作兩種編碼轉化的時候,我們希望使用者在轉化過程中可以控制一些内容。
了解transient的關鍵在于了解序列化,序列化是Java對象轉換為位元組序列。
詳細的說,就是Java對象在電腦中是存于記憶體之中的,記憶體之中的存儲方式毫無疑問和磁盤中的存儲方式不同(一個顯而易見的差別就是對象在記憶體中的存儲分為堆和棧兩部分,兩部分之間還有指針;但是存到磁盤中肯定不可能帶指針,一定是某種文本形式)。序列化和反序列化就是在這兩種不同的資料結構之間做轉化。
序列化:JVM中的Java對象轉化為位元組序列。
反序列化:位元組序列轉化為JVM中的Java對象。
了解到這裡,實作原理也是顯而易見的,隻要在處理兩個資料結構轉化的過程中,把标為transient的成員變量特殊處理一下就好了。
3、靜态成員變量不加transient關鍵字也不能被序列化
在Java中,靜态成員變量是不能被序列化的,不管有沒有transient關鍵字。
大家可以看Serializable的相關文檔:
/**
*The readObject method is responsible for reading from the stream and
* restoring the classes fields. It may call in.defaultReadObject to invoke
* the default mechanism for restoring the object's non-static and
* non-transient fields.
在所有Serializable的實作類中,都明确說明了執行個體化過程中不包含靜态成員變量和被transient修飾的關鍵字。
4、使用Externalizable自定義序列化
Externalizable這個接口也是實作序列化的,但是和Serializable有不同。首先,Externalizable是繼承Serializable的,其次Externalizable是需要程式員自己指定成員變量實作序列化的。
也就是說,使用Externalizable接口,程式員需要實作writeExternal以及readExternal這兩個方法,來自己實作序列化和反序列化。實作的過程中,需要自己指定需要序列化的成員變量,此時,static和transient關鍵詞都是不生效的,因為你重寫了序列化中的方法。
舉例:
public class XiaoMei implements Externalizable {
private String nickName;
private transient String realName;
private static String childName="美美";
public XiaoMei(){
}
public XiaoMei(String nickName,String realName){
this.nickName = nickName;
this.realName = realName;
}
public String toString(){
return String.format("XiaoMei.toString(): nickName=%s,realName=%s,childName=%s", nickName,realName,childName);
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(realName);
out.writeUTF(childName);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
realName = in.readUTF();
childName = in.readUTF();
}
}
使用上述例子中的測試代碼,輸出結果如下:
序列化前:XiaoMei.toString(): nickName=王美美,realName=王小美,childName=美美
序列化後:XiaoMei.toString(): nickName=null,realName=王小美,childName=美美
可以看出,Externalizable接口中,指定的成員變量被序列化了,不管是否有static和transient關鍵詞,但是不被指定的成員變量不能被序列化。
更多分享可以關注: