小夥子,你真的搞懂 transient 關鍵字了嗎?

先解釋下什麼是序列化

咱們的對象並不僅是存在內存中,還須要傳輸網絡,或者保存起來下次再加載出來用,因此須要Java序列化技術。java

Java序列化技術正是將對象轉變成一串由二進制字節組成的數組,能夠經過將二進制數據保存到磁盤或者傳輸網絡,磁盤或者網絡接收者能夠在對象的屬類的模板上來反序列化類的對象,達到對象持久化的目的。數組

更多序列化請參考:《關於Java序列化你應該知道的一切》這篇文章。微信

什麼是 transient?

簡單來講就是,被 transient 修飾的變量不能被序列化。網絡

具體來看下面的示例1ide

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 修飾的變量不能被序列化。this

靜態變量能被序列化嗎?

這個話題也是最近棧長的Java技術棧vip羣裏面討論的,你們對這個知識點比較模糊,我就寫了這篇文章測試總結一下。3d

若是你也想加入咱們的Java技術棧vip羣和各位大牛一塊兒討論技術,那點擊這個連接瞭解加入吧。code

那麼,到底靜態變量能被序列化嗎?廢話少說,先動手測試下吧!對象

示例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" 可獲取更多,轉載請原樣保留本信息。

相關文章
相關標籤/搜索