1. 详细介绍
transient是短暂的意思。对于transient 修饰的成员变量,在类的实例对象的序列化处理过程中会被忽略。
因此,transient变量不会贯穿对象的序列化和反序列化,生命周期仅存在于内存中而不会写到磁盘里进行持久化。
1.1 序列化
Java中对象的序列化:指将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象可以被写到数据库或文件中,也可用于网络传输。
当然,序列化后的最终目的是为了反序列化,恢复成原先的Java对象实例。所以序列化后的字节序列都是可以恢复成Java对象的,这个过程就是反序列化。
另外,实现序列化有两种方式:
- 直接或间接实现Serializable接口:JVM完全负责序列化(非transient修饰的就序列化,transient修饰的就不序列化)。
【这是一种常用的序列化方式,搭配transient使用】 - 直接或间接实现Exteranlizable接口:程序员负责序列化与反序列化(通过重写writeExternal和readExternal方法决定哪些需要被序列化,即使是即使是transient修饰没用)。
【实现Exteranlizable接口需要重写writeExternal和readExternal方法。】
注意:因为Serializable接口使用反射和元数据,这会导致相对较慢的性能。所以,Exteranlizable比Serializable的效率高
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.*;
public class Student implements Externalizable {
private transient String name;
private int age;
// setter 和 getter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// 实现方法
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name); // 设置name可序列化
out.writeInt(age); // 设置age可序列化
}
@Override
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
this.name = in.readUTF();
this.age = in.readInt();
}
}
class ExternalizableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student student = new Student();
student.setAge(27);
student.setName("Tom");
// 序列化
System.out.println("序列化前:name = " + student.getName() + ",age = " + student.getAge());
try (ObjectOutputStream outputFile = new ObjectOutputStream(new FileOutputStream(new File("./Student.txt")))) {
outputFile.writeObject(student); // write person to file
}
// 反序列化
Student studentDeserializable;
try (ObjectInputStream inputFile = new ObjectInputStream(new FileInputStream(new File("./Student.txt")))) {
studentDeserializable = (Student) inputFile.readObject();
}
System.out.println("序列化后:name = " + studentDeserializable.getName() + ",age = " + studentDeserializable.getAge());
}
}
输出:
可以看见即使是transient修饰的name,还是被序列化了。
1.2 为什么要用transient关键字?
一个类直接或间接实现了Serializable接口,那么整个类都可被序列化。但是有时,我们需要将某部分不可序列化。因此,在不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化。
比如:在持久化对象时,对于用户的密码,银行卡号等,我们不想用序列化机制来保存它,可以在这些成员变量前加上关键字transient。
注意:static修饰的变量,即类字段,天然就是不可序列化的。
2. transient使用总结
- transient关键字只能修饰变量,而不能修饰方法和类。
- 虽然类没有直接或间接继承Serializable接口,也可以使用transient关键字修饰字段。但是只有类直接或间接继承Serializable接口,可序列化,使用transient才有意义。
- 一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法被访问。
- 一个静态变量不管是否被transient修饰,均不能被序列化(如果反序列化后类中static变量还有值,则值为当前JVM中对应static变量的值)。序列化保存的是对象状态,静态变量保存的是类状态,因此序列化并不保存静态变量。
3. 使用场景
- 一些安全性的信息,比如密码等
- 某个字段值能够由其他字段计算出来。比如:圆类,有字段半径和面积,那么知道圆半径,就可以计算出圆面积,故圆面积可用stransient修饰。这样可以节省持久化所占资源空间。