天天看點

transient java_Java中關鍵字transient解析

文章轉載自如下出處:作者:醬油君

原出處:非醬油藏經閣

原文連結:非醬油藏經閣​www.nopassby.com

transient java_Java中關鍵字transient解析

在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關鍵詞,但是不被指定的成員變量不能被序列化。

更多分享可以關注: