導師讓我學習序列化和反序列化的時候才意識到自己的對java中的transient關鍵字不熟。看了很多介紹和學習筆記之後,對transient關鍵字的使用做一下總結:
如果一個對象只要實現了Serilizable接口,這個類的所有屬性和方法都會自動序列化。
1.什麼情況下需要使用transient關鍵字?
在實際開發過程中,一些類的部分屬性需要序列化,而其他屬性不需要被序列化。
比如說,如果一個用戶有一些敏感信息(如登陸密碼,銀行卡號等),爲了安全起見,不希望在網絡操作中被傳輸,這些信息對應的變量就可以加上transient關鍵字。這樣,被修飾的變量就不會實現序列化。
實現Serilizable接口,將不需要序列化的屬性前添加關鍵字transient,序列化對象的時候,這個屬性就不會序列化到指定的目的地中。transient僅存於調用者的內存中而不會寫到磁盤裏持久化。
栗子:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* @description 使用transient關鍵字不序列化某個變量。注意讀取的時候,讀取數據的順序一定要和存放數據的順序保持一致
* @author 旺仔牛奶
* 2017年8月30日
*/
public class ApplyTransient {
public static void main(String[] args) {
User user = new User();
user.setUserName("旺仔牛奶");
user.setPasswd("caka");
System.out.println("Before Serializable: ");
System.out.println("username: " + user.getUserName());
System.err.println("password: " + user.getPasswd());
//序列化操作
try {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("E:/wangzai.txt"));
os.writeObject(user); // 將User對象寫進文件
os.flush();//刷新流到文件
os.close();//關閉流
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
//反序列化操作
ObjectInputStream is = new ObjectInputStream(new FileInputStream("E:/wangzai.txt"));
user = (User) is.readObject(); // 從流中讀取User的數據
is.close();//關閉流
System.out.println("\nAfter Serializable: ");
System.out.println("username: " + user.getUserName());
System.err.println("password: " + user.getPasswd());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//User實體類
class User implements Serializable {
private static final long serialVersionUID = 8294180014912103005L;
public static String userName;
private transient String passwd;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}
輸出結果爲:
Before Serializable:
username: 旺仔牛奶
password: caka
After Serializable:
username: 旺仔牛奶
password: null
上個例子可以看出:password字段爲null,說明反序列化時根本沒有從文件中獲取到信息。(因爲類的定義中加了transient關鍵字)
2.transient關鍵字的使用總結
- 一旦變量被transient修飾,變量將不再是對象持久化的一部分,該變量內容在序列化後無法獲得訪問。
- transient關鍵字只能修飾變量,而不能修飾方法和類。注意,本地變量是不能被transient關鍵字修飾的。變量如果是用戶自定義類變量,則該類需要實現Serializable接口。(本地變量指出現在方法中,在方法中定義,在方法中使用,超出方法就不存在的變量)
- 被transient關鍵字修飾的變量不再能被序列化,一個靜態變量不管是否被transient修飾,均不能被序列化。
基於上例,再舉個例子:(在序列化之前臨時改變userName的值)
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* @description 使用transient關鍵字不序列化某個變量。注意讀取的時候,讀取數據的順序一定要和存放數據的順序保持一致
* @author 旺仔牛奶
* 2017年8月30日
*/
public class ApplyTransient {
public static void main(String[] args) {
User user = new User();
user.setUserName("旺仔牛奶");
user.setPasswd("caka");
System.out.println("Before Serializable: ");
System.out.println("username: " + user.getUserName());
System.err.println("password: " + user.getPasswd());
//序列化操作
try {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("E:/wangzai.txt"));
os.writeObject(user); // 將User對象寫進文件
os.flush();//刷新流到文件
os.close();//關閉流
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
// 在反序列化之前改變username的值
User.userName = "旺仔牛奶的牛奶";
//反序列化操作
ObjectInputStream is = new ObjectInputStream(new FileInputStream("E:/wangzai.txt"));
user = (User) is.readObject(); // 從流中讀取User的數據
is.close();//關閉流
System.out.println("\nAfter Serializable: ");
System.out.println("username: " + user.getUserName());
System.err.println("password: " + user.getPasswd());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//User實體類
class User implements Serializable {
private static final long serialVersionUID = 8294180014912103005L;
public static String userName;
private transient String passwd;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
}
輸出結果爲:
Before Serializable:
username: 旺仔牛奶
password: caka
After Serializable:
username: 旺仔牛奶的牛奶
password: null
發現userName的值並不是序列化之前的值,而是臨時修改的值。所以可見被static修飾的變量確實沒有沒序列化,是JVM中的值對應的static變量的值。
3.transient關鍵字使用的注意點
是否只要使用transient關鍵字就都不序列化?
看下面的例子:
import java.io.Externalizable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
/**
* @description 使用transient關鍵字不序列化某個變量。注意讀取的時候,讀取數據的順序一定要和存放數據的順序保持一致
* @author 旺仔牛奶
* 2017年8月30日
*/
public class ApplyTransient implements Externalizable {
private transient String content = "旺仔牛奶能被實例化嗎";
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(content);
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
content = (String) in.readObject();
}
public static void main(String[] args) throws Exception {
ApplyTransient applyTransientt = new ApplyTransient();
//序列化
ObjectOutput out = new ObjectOutputStream(new FileOutputStream(new File("wangzai")));
out.writeObject(applyTransientt);
//反序列化
ObjectInput in = new ObjectInputStream(new FileInputStream(new File("wangzai")));
applyTransientt = (ApplyTransient) in.readObject();
System.out.println(applyTransientt.content);
out.close();
in.close();
}
}
執行結果爲:
旺仔牛奶能被實例化嗎
這是爲什麼呢,不是說類的變量被transient關鍵字修飾以後將不能序列化了嗎?
在Java中,對象的序列化可以通過實現兩種接口來實現,一種是Serializable接口,則所有的序列化將會自動進行,若實現的是Externalizable接口,則沒有任何東西可以自動序列化,需要在writeExternal方法中進行手工指定所要序列化的變量,這與是否被transient修飾無關。因此第二個例子輸出的是變量content初始化的內容。