先解釋下什麼是序列化 我們的對象並不只是存在記憶體中,還需要傳輸網路,或者保存起來下次再載入出來用,所以需要Java序列化技術。 Java序列化技術正是將對象轉變成一串由二進位位元組組成的數組,可以通過將二進位數據保存到磁碟或者傳輸網路,磁碟或者網路接收者可以在對象的屬類的模板上來反序列化類的對象,達到 ...
先解釋下什麼是序列化
我們的對象並不只是存在記憶體中,還需要傳輸網路,或者保存起來下次再載入出來用,所以需要Java序列化技術。
Java序列化技術正是將對象轉變成一串由二進位位元組組成的數組,可以通過將二進位數據保存到磁碟或者傳輸網路,磁碟或者網路接收者可以在對象的屬類的模板上來反序列化類的對象,達到對象持久化的目的。
更多序列化請參考:《關於Java序列化你應該知道的一切》這篇文章。
什麼是 transient?
簡單來說就是,被 transient 修飾的變數不能被序列化。
具體來看下麵的示例1
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* @author 微信公眾號:Java技術棧
*/
public class TransientTest {
public static void main(String[] args) throws Exception {
User user = new User();
user.setUsername("Java技術棧");
user.setId("javastack");
System.out.println("\n序列化之前");
System.out.println("username: " + user.getUsername());
System.out.println("id: " + user.getId());
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("d:/user.txt"));
os.writeObject(user);
os.flush();
os.close();
ObjectInputStream is = new ObjectInputStream(new FileInputStream("d:/user.txt"));
user = (User) is.readObject();
is.close();
System.out.println("\n序列化之後");
System.out.println("username: " + user.getUsername());
System.out.println("id: " + user.getId());
}
}
/**
* @author 微信公眾號:Java技術棧
*/
class User implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private transient String id;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
輸出結果:
序列化之前
username: Java技術棧
id: javastack
序列化之後
username: Java技術棧
id: null
示例1在 id 欄位上加了 transient 關鍵字修飾,反序列化出來之後值為 null,說明瞭被 transient 修飾的變數不能被序列化。
靜態變數能被序列化嗎?
這個話題也是最近棧長的Java技術棧vip群裡面討論的,大家對這個知識點比較模糊,我就寫了這篇文章測試總結一下。
如果你也想加入我們的Java技術棧vip群和各位大牛一起討論技術,那點擊這個鏈接瞭解加入吧。
那麼,到底靜態變數能被序列化嗎?廢話少說,先動手測試下吧!
示例2:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* @author 微信公眾號:Java技術棧
*/
public class TransientStaticTest {
public static void main(String[] args) throws Exception {
User2 user = new User2();
User2.username = "Java技術棧1";
user.setId("javastack");
System.out.println("\n序列化之前");
System.out.println("username: " + user.getUsername());
System.out.println("id: " + user.getId());
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("d:/user.txt"));
os.writeObject(user);
os.flush();
os.close();
// 在反序列化出來之前,改變靜態變數的值
User2.username = "Java技術棧2";
ObjectInputStream is = new ObjectInputStream(new FileInputStream("d:/user.txt"));
user = (User2) is.readObject();
is.close();
System.out.println("\n序列化之後");
System.out.println("username: " + user.getUsername());
System.out.println("id: " + user.getId());
}
}
/**
* @author 微信公眾號:Java技術棧
*/
class User2 implements Serializable {
private static final long serialVersionUID = 1L;
public static String username;
private transient String id;
public String getUsername() {
return username;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
輸出結果:
序列化之前
username: Java技術棧1
id: javastack
序列化之後
username: Java技術棧2
id: null
示例2把 username 改為了 public static, 併在反序列化出來之前改變了靜態變數的值,結果可以看出序列化之後的值並非序列化進去時的值。
由以上結果分析可知,靜態變數不能被序列化,示例2讀取出來的是 username 在 JVM 記憶體中存儲的值。
transient 真不能被序列化嗎?
繼續來看示例3:
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;
/**
* @author 微信公眾號:Java技術棧
*/
public class ExternalizableTest {
public static void main(String[] args) throws Exception {
User3 user = new User3();
user.setUsername("Java技術棧");
user.setId("javastack");
ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(new File("javastack")));
objectOutput.writeObject(user);
ObjectInput objectInput = new ObjectInputStream(new FileInputStream(new File("javastack")));
user = (User3) objectInput.readObject();
System.out.println(user.getUsername());
System.out.println(user.getId());
objectOutput.close();
objectInput.close();
}
}
/**
* @author 微信公眾號:Java技術棧
*/
class User3 implements Externalizable {
private static final long serialVersionUID = 1L;
public User3() {
}
private String username;
private transient String id;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public void writeExternal(ObjectOutput objectOutput) throws IOException {
objectOutput.writeObject(id);
}
@Override
public void readExternal(ObjectInput objectInput) throws IOException, ClassNotFoundException {
id = (String) objectInput.readObject();
}
}
輸出結果:
null
javastack
示例3的 id 被 transient 修改了,為什麼還能序列化出來?那是因為 User3 實現了介面 Externalizable,而不是 Serializable。
在 Java 中有兩種實現序列化的方式,Serializable 和 Externalizable,可能大部分人只知道 Serializable 而不知道 Externalizable。
這兩種序列化方式的區別是:實現了 Serializable 介面是自動序列化的,實現 Externalizable 則需要手動序列化,通過 writeExternal 和 readExternal 方法手動進行,這也是為什麼上面的 username 為 null 的原因了。
transient 關鍵字總結
1)transient修飾的變數不能被序列化;
2)transient只作用於實現 Serializable 介面;
3)transient只能用來修飾普通成員變數欄位;
4)不管有沒有 transient 修飾,靜態變數都不能被序列化;
好了,棧長花了半天時間,終於整理完了。如果對你有幫助,那就轉發分享一下吧!如果你也想加入我們的Java技術棧vip群和各位大牛一起討論技術,那點擊這個鏈接瞭解加入吧
另外,棧長已經整理了大量 Java 系列核心技術知識點文章,關註Java技術棧微信公眾號,在後臺回覆關鍵字:java,即可獲取最新版。
本文原創首發於微信公眾號:Java技術棧(id:javastack),關註公眾號在後臺回覆 "java" 可獲取更多,轉載請原樣保留本信息。