当前位置: 首页 > article >正文

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

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

1 transient 的作用及使用方法

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

示例

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;
    }
}

输出结果:

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

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

2 transient 使用小结

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

示例

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;
    }
}

运行结果:

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

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

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

思考下面的例子:

public class ExternalizableTest implements Externalizable {
    private transient String content = "是的,我将会被序列化,不管我是否被transient关键字修饰";

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(content);
    }

    @Override
    public 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();
    }
}

输出结果:

是的,我将会被序列化,不管我是否被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.kler.cn/a/382837.html

相关文章:

  • CSS学习之Grid网格布局基本概念、容器属性
  • K8S集群管理常用命令
  • AI笔筒操作说明及应用场景
  • 计算机网络——网络层导论
  • (11)(2.1.6) Hobbywing DroneCAN ESC(二)
  • 企业数字化转型的架构治理策略:核心问题、深度分析与优化路径
  • WebGUI之Gradio:Gradio 5的简介、安装和使用方法、案例应用之详细攻略
  • Redis - List 列表
  • 使用Golang实现开发中常用的【并发设计模式】
  • 【系统集成项目管理工程师教程】第12章 执行过程组
  • 关于基于AGI和大模型技术下养老服务高质量发展解决方案项目,以及实现代码过程实战
  • OBOO鸥柏丨传媒广告行业的创新应用解决数字技术短板
  • 软件对象粒度控制与设计模式在其中作用的例子
  • ubuntu 22.04 server 格式化 磁盘 为 ext4 并 自动挂载 LTS
  • 计算网络信号
  • git 工具原理
  • PN结特性及反向饱和电流与反向漏电流详解
  • Halcon OCR 字体训练
  • DevOps业务价值流:需求设计最佳实践
  • 【命令操作】Linux三剑客之awk详解 _ 统信 _ 麒麟 _ 方德
  • C/C++」C++类型转换 之 dynamic_cast 操作符
  • C#枚举实战:定义、使用及高级特性解析
  • [ DOS 命令基础 2 ] DOS 命令详解-网络相关命令
  • Qt(openCV的应用)
  • liunx系统介绍
  • 蓝禾,汤臣倍健,三七互娱,得物,顺丰,快手,途游游戏25秋招内推