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

createh55个月前 (12-29)技术教程63

先解释下什么是序列化

我们的对象并不只是存在内存中,还需要传输网络,或者保存起来下次再加载出来用,所以需要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中的this关键字吗,那来看这篇就够了

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

三十四、Java中的final关键字 java中final关键字的用途

Java中的final关键字是一种修饰符,它有着多种用途,主要应用在变量、方法和类上,以指示不可变性或不可覆盖性。final 关键字修饰不同元素的作用Java元素作用变量当final修饰基本类型变量时...

Java 关键字之 native 详解 java native access

本篇博客我们将介绍Java中的一个关键字——native。native 关键字在 JDK 源码中很多类中都有,在 Object.java类中,其 getClass() 方法、hashCode()方法、...

长知识了!Java 关键字 transient 还能这么用

前言最近在看 HashMap 源代码的时候,发现链表 table 数组采用了transient 关键字,笔者当时感觉对 transient 关键字即陌生但又有似曾相识,所以花了一些时间简要的总结了下使...

Java Final 关键字详解 java中final关键字的用途

#大有学问# #java#在Java编程语言中,final关键字是一个重要的概念,它可以用于修饰类、方法和变量。本文将详细介绍final关键字的用途、特点及使用场景,帮助您更深入地理解Java中的f...

java基础之static关键字 java 中static

我真是个莫得感情的更新机器关于static的使用,之前学习接触过的场景如下:静态修饰符,(作用于变量,方法,代码块)有位大神说过:“static方法就是没有this的方法。在static方法内部不能调...