在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关键字使用小记