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 属性没有被序列化
}

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

类变量不会被序列化

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

public class MyClass implements Serializable {
    private static MyClass myVariable; // 这是一个类变量
    // 这是实例变量
    private MyClass instanceVariable;

    // 构造方法、getter和setter等代码
}

在这个例子中,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));
    }
}

可见,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
    }
}

需要说明的是,并不是所有的框架都支持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关键字使用小记

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值