前言
最近在看 HashMap
源代码的时候,发现链表 table
数组采用了transient
关键字,笔者当时感觉对 transient
关键字即陌生但又有似曾相识,所以花了一些时间简要的总结了下使用transient
关键字的一些基本常识,希望对你们也有些帮助,让我们一起进步,一起牛逼吧。
transient 关键字的定义
说起 transient
关键字,不得不提对象的 序列化
的,因为我们常常需要在网络上以对象(数据)的二进制方式传输数据,这里涉及到发送方序列化对象,接收方反序列化对象的过程。
那什么是序列化/反序列化?
“
Java
中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象可以被写到数据库或文件中,也可用于网络传输。一般地,当我们使用缓存cache
(内存空间不够有可能会本地存储到硬盘)或远程调用rpc
(网络传输)的时候,经常需要让实体类实现Serializable
接口,目的就是为了让其可序列化。当然,序列化后的最终目的是为了反序列化,恢复成原先的Java对象实例。所以序列化后的字节序列都是可以恢复成Java对象的,这个过程就是反序列化。
在对象的序列化/反序列化过程中,我们经常有这种需求,就是非必要字段不必进行序列化。
例如有一个对象有三个字段 field1
、field2
、field3
,发送方不想让字段 field3
被序列化,因为这里面可能涉及到一些敏感信息不想被接收方知道,那有没有办法解决这个问题呢?
其实聪明的 Java
作者早就为我们量身定做了 transient
关键字!
简单来说,被 transient
关键字修饰过的成员属性不能被序列化,transient
关键字只能修饰变量,而不能修饰方法和类。
transient 关键字的约定
- 约定一、只能修饰变量而不能修饰方法和类。注意本地变量是不能被
transient
关键字修饰的。 - 约定二、被
transient
关键字修饰过的属性不能被序列化,也就是说被transient
修饰过的属性,在对对象序列化后,是无法访问到该属性的。 - 约定三、静态变量不管有无被
transient
修饰过,不能被序列化。
transient 关键字的使用场景
首先,我们看个例子,有个产品对象 Product
,包括价格
、数量
、总价
三个字段,那么总价
可以通过 价格
乘以 数量
推导出来。
我们以查询某个产品 API
接口为例,通过产品 ID
,查询返回一个产品对象。
public class Product {
private int amounts;
private int price;
private int sum;
}
通过 Gson
序列化后把 json
数据返回给前端,这时的 sum
字段是没有经过 transient
修饰过的,所以能够正常序列化。
{"amounts":3,"price":2,"sum":6}
假设我们的产品对象
Product
的 sum
属性加上 transient
关键字修饰:
public class 产品对象 Product 的 sum {
private int amounts;
private int price;
private transient int sum;
}
然后我们试着初始化 Product
,并用 Gson
的 toJson()
方法序列化输出 json
格式的结果。
public static void main(String[] args) {
Product p = new Product();
p.setAmounts(3);
p.setPrice(2);
p.setSum(p.getAmounts() * p.getPrice());
String json = new Gson().toJson(p);
System.out.println(json);
}
这时控制台是没有打印出 sum
字段的。
{"amounts":3,"price":2}
我们看到,sum
属性被 transient
修饰后,是不会被 Gson
序列化输出的,这里就引出了使用 transient
关键字一个很重要的概念:对象属性推导。
对象属性推导
“如果一个对象的属性值可以通过其他属性或者方法推理出来的,那么该属性就没必要被序列化了。
借此我们以 Gson
来分析被 transient
修饰过的属性不能被序列化过程。
首先,调用 Gson
的 toJson()
方法,传入 Product
对象。
new Gson().toJson(product)
根据传入的产品对象,获取 Product
对象的 class
类型:typeOfSrc
,最后 找到对应的对象解析适配器工厂。
toJson(Object src, Type typeOfSrc, JsonWriter writer)
TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc));
for (TypeAdapterFactory factory : factories) {
// 得到ReflectiveTypeAdapterFactory
TypeAdapter<T> candidate = factory.create(this, type);
}
通过适配器 ReflectiveTypeAdapterFactory
工厂的 create()
方法,我们找到 getBoundFields
方法。
new Adapter<T>(constructor, getBoundFields(gson, type, raw));
for (Field field : fields) {
boolean serialize = excludeField(field, true);
boolean deserialize = excludeField(field, false);
...
}
这个方法做了两件事情:
- 剔除被
transient
关键字修饰过的属性 - 筛选出可序列化的属性
通过 excludeField()
方法,剔除被 transient
修饰过的属性,其规则是通过位运算 "&"
判断 modifiers
属性与对象属性的 field.getModifiers()
的值是否一致,来证明该属性是否被 transient
修饰过,如果是为真,表示剔除该属性,不进行序列化。
public boolean excludeField(Field field, boolean serialize) {
// 通过 if 判断 modifiers 属性
// private int modifiers = Modifier.TRANSIENT | Modifier.STATIC;
if ((modifiers & field.getModifiers()) != 0) {
return true;
}
}
另外根据 modifiers
属性定义 Modifier.TRANSIENT | Modifier.STATIC
两种类型,一种是 tranient
,另一种是 static
静态类型。
Modifier.STATIC:静态类型
由约定三、我们知道,静态变量不会被序列化。
代码 debug
到此,我们已经知道 Gson
是如何证明对象是否存在被 transient
修饰过属性以及如何过滤掉的完整过程。
被 transient 关键字修饰过得变量真的不能被序列化嘛?
想要解决这个问题,首先还要再重提一下对象的序列化方式,Java
序列化提供两种方式。
一种是实现 Serializable
接口,另一种是实现 Exteranlizable
接口。
实现 Exteranlizable
接口需要重写 writeExternal
和 readExternal
方法,它的效率比 Serializable
高一些,并且可以决定哪些属性需要序列化(即使是 transient
修饰的),但是对大量对象,或者重复对象,则效率低。
从上面的这两种序列化方式,我想你已经看到了,使用 Exteranlizable
接口实现序列化时,我们自己指定那些属性是需要序列化的,即使是 transient
修饰的。下面就验证一下
首先我们定义 User1
类:这个类是被 Externalizable
接口修饰的
然后我们就可以测试了
上面,代码分了两个方法,一个是序列化,一个是反序列化。里面的代码和一开始给出的差不多,只不过,User1
里面少了 age
这个属性。
然后看一下结果:
结果基本上验证了我们的猜想,也就是说,实现了 Externalizable
接口,哪一个属性被序列化是我们手动去指定的,即使是 transient
关键字修饰也不起作用。
transient 关键字总结
- 通过常用的
Gson
方式来验证tranient
关键字不能序列化的使用场景。 - 通过实现了
Externalizable
接口,如果手动去指定属性序列化的,即使是transient
关键字修饰也不起作用。 - 另外,还可以通过
java
的io
包下的ObjectInputStream
和ObjectOutputStream
两个对象输入输出流也可以验证,这里就不再做赘述,感兴趣的朋友可以在网上找找例子。
参考
- https://www.cnblogs.com/chenpt/p/9415249.html
- https://blog.csdn.net/u012723673/article/details/80699029
- https://baijiahao.baidu.com/s?id=1636557218432721275&wfr=spider&for=pc
转载自:https://cloud.tencent.com/developer/article/1808869
标签:Java,对象,关键字,transient,修饰,序列化,属性 From: https://www.cnblogs.com/sunny226/p/17219576.html