小伙子,你真的搞懂 transient 关键字了吗?

先解释下什么是序列化

我们的对象并不只是存在内存中,还需要传输网络,或者保存起来下次再加载出来用,所以需要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 系列核心技术知识点文章,关注Java技术栈阅读吧。

相关文章

Java语言static关键字详解 java 中static

在Java语言中,static关键字是一个非常重要的修饰符,可以创建独立于具体对象的域变量或者方法。也就是实现即使没有创建对象,也能使用属性和调用方法。另一个比较关键的作用就是 用来形成静态代码块以优...

Java关键字:final,static,this,super

1. final 关键字:final 关键字,意思是最终的、不可改变的,初始化之后就不能再次修改 ,用来修饰类、方法和变量,具有以下特点:final 修饰的类不能被继承,final类中的所有成员方法都...

还没弄明白Java中的this关键字吗,那来看这篇就够了

今天在上课时,冉冉大妹纸拉着我问:小哥哥,小哥哥,听说你在学Java,那你知道this关键字吗?我:啊?this啊? (完了完了,学习的时候学的什么也不是,这下被问到了,还是个妹纸,答不上来岂不尴尬)...

Java 的标识符和关键字 java 的标识符和关键字一样吗

Java 的标识符和关键字标识符的概念:标识符就是我们在编程中使用的“名字”。给类、接口、方法、变量、常量、包名起的相对唯一的名字。在编程语言中,不管是什么样的编程语言都会有标识符。并且都有差不多的规...

深入理解 Java 中的 volatile 关键字

在 Java 编程的神秘领域中,volatile关键字犹如一把神奇的钥匙,为多线程编程带来关键的保障。现在,让我们更深入地理解这个神秘的关键字以及其背后的重要机制 —— 内存屏障,同时探讨如何保证并发...

Java 中你绝对没用过的一个关键字?

这节课给大家介绍一个 Java 中的一个关键字 Record,那 Record 关键字跟不可变类有什么关系呢?看完今天的文章你就知道了。友情提示 Record 关键字在 Java14 过后才支持的,所...