最近在看HashMap源代码的时候,发现链表table数组采用了transient关键字,笔者当时感觉对transient关键字很陌生但又似曾相识,哪里用到过?所以对java关键字 transient总结了下。
Java关键字:transient的定义
简单来说,被transient修饰过的成员属性,不能被序列化。
我们以查询某个产品API接口为例,通过产品ID,查询返回一个产品对象,通过json序列化后把json数据返回给前端。
public class Product {
private int amounts;
private int price;
private int sum;
}
json
{"amounts":3,"price":2,"sum":6}
Java关键字:transient的约定
约定一、只能修饰变量
而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。
约定二、被transient关键字修饰过的属性不能被序列化
也就是说,被transient修饰过的属性,在对对象序列化后,是无法访问到该属性的。
约定三、静态变量不管有无被transient修饰过,不能被序列化
Java关键字:transient使用场景
对象属性推导
如果一个对象的属性值,可以通过其他属性或者方法推理出来的,那么没该属性没必要被序列化了。
例如有个产品对象(Product)包括价格、数量、总价三个字段,那么总价可以通过价格乘以数量推导出来。
@Data
static class Product {
private int amounts;
private int price;
private transient int sum;
}
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);
}
输出结果:
{"amounts":3,"price":2}
我们看到,sum属性被transient修饰后,不会被Gson序列化输出。
我们来分析下Gson序列化过程:Gson序列化源码分析,首先调用toJson方法,传入Product对象:
new Gson().toJson(p)
根据传入的对象,获取对象的class类型:typeOfSrc,找到对应的对象解析适配器工厂
toJson(Object src, Type typeOfSrc, JsonWriter writer)
TypeAdapter> adapter = getAdapter(TypeToken.get(typeOfSrc));
for (TypeAdapterFactory factory : factories) {
// 得到ReflectiveTypeAdapterFactory
TypeAdapter candidate = factory.create(this, type);
}
通过适配器ReflectiveTypeAdapterFactory工厂的create方法,我们找到getBoundFields方法,这个方法做了两件事情
1、剔除被transient关键字修饰过的属性。
2、筛选出可序列化的属性
new Adapter(constructor, getBoundFields(gson, type, raw));
for (Field field : fields) {
boolean serialize = excludeField(field, true);
boolean deserialize = excludeField(field, false);
...
}
通过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修饰过属性以及如何过滤掉的呢。
总结
通过常用的Gson方式来验证tranient关键字,还可以通过java的io包下的 ObjectInputStream和ObjectOutputStream两个对象输入输出流也可以验证的,这里不做赘述,网上例子很多。