Java 中的 transient 关键字:深入解析与实战

devtools/2024/11/14 6:28:21/

在 Java 编程中,transient 关键字是一个非常有用的工具,尤其是在处理对象序列化时。尽管 transient 关键字在日常开发中可能不常被使用,但了解它的作用和使用场景对于提升代码的安全性和性能至关重要。本文将深入探讨 transient 关键字的作用、使用方法以及一些注意事项。

1 transient 的作用及使用方法

在 Java 中,一个对象只要实现了 Serializable 接口,它就可以被序列化。然而,在实际开发过程中,我们常常会遇到这样的情况:一个类的某些字段需要序列化,而另一些字段则不需要。例如,用户的敏感信息(如密码、银行卡号等)为了安全起见,不希望在网络操作中传输或持久化到磁盘文件中。这时,我们就可以使用 transient 关键字来标记这些字段。

示例

java">public class TransientTest {public static void main(String[] args) {User user = new User();user.setUsername("沉默王二");user.setPasswd("123456");System.out.println("read before Serializable: ");System.out.println("username: " + user.getUsername());System.err.println("password: " + user.getPasswd());try {ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("user.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("user.txt"));user = (User) is.readObject(); // 从流中读取User的数据is.close();System.out.println("\nread after 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();}}
}class User implements Serializable {private static final long serialVersionUID = 8294180014912103005L;  private 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;}
}

输出结果:

java">read before Serializable:
username: 沉默王二
password: 123456 
read after Serializable:
username: 沉默王二
password: null

从输出结果可以看出,password 字段为 null,说明反序列化时根本没有从文件中获取到该字段的信息。

2 transient 使用小结

  1. 一旦字段被 transient 修饰,该成员变量将不再是对象持久化的一部分,该变量的值在序列化后无法访问。
  2. transient 关键字只能修饰字段,而不能修饰方法和类。
  3. transient 关键字修饰的字段不能被序列化,一个静态变量(static 关键字修饰)不管是否被 transient 修饰,均不能被序列化。

示例

java">public class TransientTest {public static void main(String[] args) {User user = new User();user.setUsername("沉默王二");user.setPasswd("123456");System.out.println("read before Serializable: ");System.out.println("username: " + user.getUsername());System.err.println("password: " + user.getPasswd());try {ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("user.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("user.txt"));user = (User) is.readObject(); // 从流中读取User的数据is.close();System.out.println("\nread after 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();}}
}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;}
}

运行结果:

java">read before Serializable:
username: 沉默王二
password: 123456 
read after Serializable:
username: 沉默王三
password: null

从结果可以看出,username 字段在反序列化后变成了 沉默王三,这证明了 static 修饰的字段不能被序列化。

3 transient 修饰的字段真的不能被序列化?

思考下面的例子:

java">public class ExternalizableTest implements Externalizable {private transient String content = "是的,我将会被序列化,不管我是否被transient关键字修饰";@Overridepublic void writeExternal(ObjectOutput out) throws IOException {out.writeObject(content);}@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {content = (String) in.readObject();}public static void main(String[] args) throws Exception {ExternalizableTest et = new ExternalizableTest();ObjectOutput out = new ObjectOutputStream(new FileOutputStream(new File("test")));out.writeObject(et);ObjectInput in = new ObjectInputStream(new FileInputStream(new File("test")));et = (ExternalizableTest) in.readObject();System.out.println(et.content);out.close();in.close();}
}

输出结果:

java">是的,我将会被序列化,不管我是否被transient关键字修饰

这是为什么呢?不是说 transient 关键字修饰的字段不能序列化吗?

这是因为我们使用了 Externalizable 接口而不是 Serializable 接口。在 Java 中,对象的序列化可以通过实现两种接口来实现:

  • Serializable 接口:所有的序列化将会自动进行。
  • Externalizable 接口:需要在 writeExternal 方法中指定要序列化的字段,与 transient 关键字修饰无关。

因此,例子中输出的是变量 content 的内容,而不是 null

4 小结

transient 关键字用于修饰类的成员变量,在序列化对象时,被修饰的成员变量不会被序列化和保存到文件中。其作用是告诉 JVM 在序列化对象时不需要将该变量的值持久化,这样可以避免一些安全或者性能问题。但是,transient 修饰的成员变量在反序列化时会被初始化为其默认值(如 int 类型会被初始化为 0,引用类型会被初始化为 null),因此需要在程序中进行适当的处理。

transient 关键字和 static 关键字都可以用来修饰类的成员变量。其中,transient 关键字表示该成员变量不参与序列化和反序列化,而 static 关键字表示该成员变量是属于类的,不属于对象的,因此不需要序列化和反序列化。

SerializableExternalizable 接口中,transient 关键字的表现也不同:

  • Serializable 中,transient 表示该成员变量不参与序列化和反序列化。
  • Externalizable 中,transient 不起作用,因为 Externalizable 接口需要实现 readExternalwriteExternal 方法,需要手动完成序列化和反序列化的过程。

通过深入理解 transient 关键字,我们可以更好地控制对象的序列化过程,从而提升代码的安全性和性能。

5 思维导图

在这里插入图片描述

6 参考链接

招银面试官:说说 Java transient 关键字吧


http://www.ppmy.cn/devtools/133244.html

相关文章

【网络-交换机】生成树协议、环路检测

路由优先级 路由优先级决定了在多种可达的路由类型中,哪种路由将被用来转发数据包。路由优先级值越低,对应路由的优先级越高,优先级值255表示对应的路由不可达。一般情况下,静态路由的优先级为1,OSPF路由优先级为110&a…

gan的所有种类,人工智能 机器学习,gan的所有算法

参考最全汇总GAN网络及其各种变体(附论文及代码实现)_gan网络代码-CSDN博客 这是我找到的关于您提问的答案: 1. **GAN类型**: Auxiliary Classifier GAN **参考论文**: 《Conditional Image Synthesis With Auxiliary Classifier GANs》 **代码地址**: …

基于Redis缓存机制实现高并发接口调试

创建接口 这里使用的是阿里云提供的接口服务直接做的测试,接口地址 curl http://localhost:8080/initData?tokenAppWithRedis 这里主要通过参数cacheFirstfalse和true来区分是否走缓存,正常的业务机制可能是通过后台代码逻辑自行控制的,这…

Zookeeper笔记

一、Zookeeper概述 - 定义:Zookeeper是一个分布式的、开源的分布式应用程序协调服务。它主要用于管理和协调分布式系统中的各种服务,提供诸如配置管理、命名服务、分布式锁等功能。 - 应用场景举例:在Hadoop生态系统中,用于协调H…

舜宇光学科技入职测评:北森商业推理40分钟28题真题解析、网盘资料下载、答题技巧

舜宇光学科技的北森商业推理测评主要考察应聘者的商业推理能力,具体内容包括以下几个方面: 1. **言语能力题**:主要考察语言理解和表达能力,包含阅读理解、完形填空等。 2. **图形推理题**:给出一组图形,…

rabbitMq怎么保证消息不丢失?消费者没有接收到消息怎么处理

在使用RabbitMQ时,保证消息不丢失以及处理消费者未接收到消息的情况可以通过以下几个方法: 1. 确保消息的持久化 队列持久化:在声明队列时将其设置为持久化(durabletrue),这样RabbitMQ在重启后也会保留队…

Java中 ==和equals的区别?

目录 1. 运算符 用法 对象的比较 基本数据类型的比较 2. equals() 方法 用法 equals() 的重写 3. 和 equals() 的实际区别 示例: 和 equals() 的区别 小结: 1. 运算符 用法 是 比较运算符,它用于比较两个对象的 引用是否相同&…

软考中级 软件设计师 上午考试内容笔记(个人向)Part.3

软考上午考试内容 7. 网络安全 威胁名称描述恶意攻击(ARP)所截获的合法通信数据拷贝,出于非法的目的而被重新发送。拒绝服务(DOS)对信息或其它资源的合法访问被无条件地阻止。窃听用户可利用合法或非法的手段窃取系统中…