最近在看源码的时候看到一个关键字transient
,之前对这个字没有印象,所以就去看了一下它的作用。
transient的作用
首先放上来着维基百科的解释:
Java 提供自动序列化,需要以
java.io.Serializable
接口的实例来标明对象。实现接口将类别标明为“可序列化”,然后Java在内部处理序列化。在Serializable
接口上并没有预先定义序列化的方法,但可序列化类别可任意定义某些特定名称和签署的方法,如果这些方法有定义了,可被调用运行序列化/反序列化部分过程。该语言允许开发人员以另一个Externalizable
接口,更彻底地实现并覆盖序列化过程,这个接口包括了保存和恢复对象状态的两种特殊方法。在默认情况下有三个主要原因使对象无法被序列化。其一,在序列化状态下并不是所有的对象都能获取到有用的语义。例如,
Thread
对象绑定到当前Java虚拟机的状态,对Thread
对象状态的反序列化环境来说,没有意义。其二,对象的序列化状态构成其类别兼容性缔结(compatibility contract)的某一部分。在维护可序列化类别之间的兼容性时,需要额外的精力和考量。所以,使类别可序列化需要慎重的设计决策而非默认情况。其三,序列化允许访问类别的永久私有成员,包含敏感信息(例如,密码)的类别不应该是可序列化的,也不能外部化。上述三种情形,必须实现Serializable
接口来访问Java内部的序列化机制。标准的编码方法将字段简单转换为字节流。原生类型以及永久和非静态的对象引用,会被编码到字节流之中。序列化对象引用的每个对象,若其中未标明为
transient
的字段,也必须被序列化;如果整个过程中,引用到的任何永久对象不能序列化,则这个过程会失败。开发人员可将对象标记为暂时的,或针对对象重新定义的序列化,来影响序列化的处理过程,以截断引用图的某些部分而不序列化。Java并不使用构造函数来序列化对象。
从上面的最后一段可以了解,如果没有添加transient
关键字,则会被进行序列化。也就是说添加了这个关键字后就不会被序列化。
接下来我们将用一个例子来测试一下
简单例子
首先定义一个实体类,用来被序列化。
1 | public class UserDomain implements Serializable { |
定义了3个字段,其中的name字段被添加上了transient
关键字。
然后我们继续编写一个测试类
1 | public class TransientTest { |
我们通过构造函数创建了一个实体变量。然后将它打印出来。
接下来我们将它序列化之后写到文件中。
再将它从文件中读取出来,然后转换为实体类型。
将它再次打印出来,通过控制台打印查看区别。
1 | 序列化之前的打印 |
可以看出,在序列化之前所有的字段都被打印出来,然后经过一次序列化后我们添加transient
的字段就没有信息了。所以添加了这个关键字后就可以取消序列化了。
使用小结
- 一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法访问、
- transien关键字只能修饰变量,而不能修饰方法和类。
- 一个静态变量不管是否被transient修饰,均不能被序列化。因为静态变量会被加载到jvm中,并且仅加载一次。所以它不管有没有
transient
关键字都不会被序列化。 - 并不是添加了
transient
之后都不会被序列化,只是在Serializable
接口下会这样,如果实现的是Externalizable
它还是会被序列化。