• transient关键字使用说明


    在Java中,transient关键字用于表示某个字段不需要序列化。如一些敏感信息(手机号、密码等),不需要序列化。需要说明的是,transient关键字只能修饰变量,不能修饰方法或类。对于类变量(静态变量,由static关键字修饰)不能被序列化,所以无需额外添加transient关键字。而使用final修饰的变量,不会影响transient关键字的作用效果。特别地,如果基于Externalizable 接口实现序列化,因为需要手动指定需要序列化的属性,所以不受transient 关键字的影响。也就是说,对于基于Externalizable 接口序列化的对象,不受transient 关键字的影响。接下来通过代码来验证以上结论。

    使用transient关键字标记不需要序列化的字段

    在Java中,transient关键字用于表示某个字段不需要序列化。注意,transient关键字仅能标记某个字段不需要序列化,并没有实现相关的能力。在实际的应用中发现,所有的三方件在序列化时,都不会对transient关键字标记的字段进行序列化。下面举例说明。

    public void testTransientByFastJson() {
        // 基于 fastjson 1.2.67 验证
        User user = new User();
        user.setUsername("foo");
        user.setPassword("123456789");
        System.out.println(JSON.toJSONString(user));   // 输出:{"username":"foo"}
        User testUser = JSON.parseObject("{\"username\":\"foo\",\"password\":\"123456789\"}", User.class);
        System.out.println(testUser.getPassword());  // 输出:"123456789"
    }
    
    public void testTransientByStream() throws IOException, ClassNotFoundException {
        User user = new User();
        user.setUsername("foo");
        user.setPassword("123456789");
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(user);
        oos.close();
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        User deserializedUser = (User) ois.readObject();
        ois.close();
        System.out.println(deserializedUser.getUsername()); // 输出 "foo"
        System.out.println(deserializedUser.getPassword()); // 输出 null,因为 password 属性没有被序列化
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    上面的代码,分别使用fastjson(阿里巴巴开源三方件)和JDK自带的流处理工具类实现了序列化,可以看到transient关键字标记的字段不会序列化到结果中。同时,也注意到,在反序列化时,fastjson三方件会恢复transient关键字标记的字段。也就是说,不同框架对transient关键字标记的字段的反序列化的恢复,可能有差异,要验证后使用。

    类变量不会被序列化

    序列化和反序列化是针对对象而言,类变量(static 关键字修饰)不是对象的状态,因此不会被序列化。它们是类的一部分,不是单独的对象。

    public class MyClass implements Serializable {
        private static MyClass myVariable; // 这是一个类变量
        // 这是实例变量
        private MyClass instanceVariable;
    
        // 构造方法、getter和setter等代码
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这个例子中,myVariable 是一个类变量,而 instanceVariable 是一个实例变量。当 MyClass 的对象被序列化和反序列化时,只有 instanceVariable 会被序列化,而 myVariable 不会被序列化。

    final修复的变量不影响transient的作用效果

    在Java中,final关键字不会直接影响对象的序列化。序列化是一个过程,用于将对象的状态信息转换为字节流,以便可以将其存储到磁盘或通过网络发送到另一个运行Java的进程。而final关键字在Java中表示一个不可变的变量。如果一个对象是final,那么它不能被另一个对象引用,这意味着它不能被修改。然而,即使对象是final,它仍然可以被序列化和反序列化。简言之,final关键字本身并不会直接影响对象的序列化过程。

    // 定义实体
    public class User implements Serializable {
        // 使用final修复变量
        private final String code;
        private String username;
        private transient String password;
        public User() {
            this.code = "001";
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public String getCode() {
            return code;
        }
    }
    // 使用实体
    public class TransientDemo {
        public void testTransientByFastJson() {
            User user = new User();
            user.setUsername("foo");
            user.setPassword("123456789");
            User.setType("local");
            // {"code":"001","username":"foo"}
            System.out.println(JSON.toJSONString(user));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    可见,final关键字本身并没有影响对象的序列化,使用final修饰的变量仍然可以序列化。

    基于Externalizable接口实现的序列化不受transient影响

    Java中的Externalizable接口是用于自定义对象序列化的方式。通过实现Externalizable接口,可以控制对象的序列化和反序列化过程,以及在序列化和反序列化时写入和读取哪些字段。实现Externalizable接口需要实现两个方法:writeExternal()和readExternal()。其中,writeExternal()方法用于将对象的字段写入输出流,而readExternal()方法用于从输入流中读取字段并设置到对象中。

    // 定义实体并实现Externalizable接口
    public class Person implements Externalizable {
        private transient String name;
        private int age;
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return this.name;
        }
        public int getAge() {
            return this.age;
        }
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeUTF(name);
            out.writeInt(age);
        }
        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            name = in.readUTF();
            age = in.readInt();
        }
    }
    // 使用实体
    public class TransientDemo {
        public void testExternalWithTransient() throws IOException, ClassNotFoundException {
            Person person = new Person("jack", 25);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(person);
            oos.close();
            System.out.println(bos.toByteArray());
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            Person deserializedPerson = (Person) ois.readObject();
            ois.close();
            System.out.println(deserializedPerson.getName()); // 输出 "jack",即使已经使用transient关键字修饰了字段,也不影响序列化
            System.out.println(deserializedPerson.getAge()); // 输出 25
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    需要说明的是,并不是所有的框架都支持Externalizable接口方式,以fastjson为例,上述Person实例的反序列化结果为"{}",在使用的时候,一定要提前验证好,确保所使用的序列化框架支持哪种序列化实现。

    参考

    https://zhuanlan.zhihu.com/p/362241118 Java 关键字 transient
    https://blog.csdn.net/qq_44543508/article/details/103232007 java中的transient关键字详解
    https://www.runoob.com/w3cnote/java-transient-keywords.html Java transient 关键字
    https://www.baidu.com/ 百度AI搜索
    https://www.cnblogs.com/lanxuezaipiao/p/3369962.html Java transient关键字使用小记

  • 相关阅读:
    nmap挖掘服务器漏洞(黑客攻击), script详解
    土地利用强度(LUI)综合指数
    倒计时列表实现(小程序端&Vue)
    UG\NX二次开发 隐藏菜单、隐藏菜单中的按钮
    三维大场景管理-3Dtiles规范
    线性同余方程(扩展欧几里得acwing878)
    电饭锅一会儿通电一会儿不通电【检修原因】
    C++图书管理系统(管理员-读者)
    Appium混合页面点击方法tap的使用
    【Python数据科学快速入门系列 | 02】创建ndarray对象的十多种方法
  • 原文地址:https://blog.csdn.net/wangxufa/article/details/132721092