面试官:告诉我为什么static和transient关键字修饰的变量不能被序列化?
一、写在开头
在上一篇学习序列化的文章中我们提出了这样的一个问题:
“如果在我的对象中,有些变量并不想被序列化应该怎么办呢?”
当时给的回答是:不想被序列化的变量我们可以使用transient或static关键字修饰;transient 关键字的作用是阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复;而static关键字修饰的变量并不属于对象本身,所以也同样不会被序列化!
当时没有解释具体为什么static和transient 关键字修饰的变量就不能被序列化了,这个问题实际上在很多大厂的面试中都可能会被问及。我们今天在这篇中进行解释吧。
二、案例演示
我们先通过一个实战案例,去看一看用static和transient 关键字修饰后的变量,序列化与反序列化后的现象。
public class TestService {
public static void main(String[] args) throws IOException {
//初始化对象信息
Person person = new Person();
person.setName("JavaBuild");
person.setAge(30);
System.out.println(person.getName()+" "+person.getAge());
//序列化过程
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("E:\\person.txt"));) {
objectOutputStream.writeObject(person);
} catch (IOException e) {
e.printStackTrace();
}
person.par1 = "序列化后静态字段";
//反序列化过程
try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("E:\\person.txt"));) {
Person p = (Person) objectInputStream.readObject();
System.out.println(p);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Person implements Serializable{
private static final long serialVersionUID = 8711922740433840551L;
private String name;
private int age;
public static String par1 = "静态字段";
transient String par2 = "临时字段";
transient int high = 175;
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 String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", par1=" + par1 +
", high=" + high +
", par2='" + par2 + '\'' +
'}';
}
}
在Person类中,我们定义了两个正常的属性,姓名与年龄,同时呢,我们也分别定义了一个静态字段和两个临时字段,输出结果为:
JavaBuild 30
Person{name='JavaBuild', age=30, par1=序列化后静态字段, high=0, par2='null'}
对于使用static关键字修饰的par1来说,在整个序列化过程中,它并未参与,原因是:我们在序列化与反序列化之间插入了属性的重新赋值操作,最后输出中打印出的是最新赋值,说明仅是调用了实例对象的属性值,而不是反序列化的结果。
而对于transient 关键字修饰high和par2,在序列化时直接被忽略了。从输出结果看就更加的明了了,int类型直接还原为默认值0,而String类型直接为null。
什么原因呢?咱们继续往下看。
三、源码分析
在之前的文章中,我们已经解释过了,在序列化时Serializable只是作为一种标识接口,告诉程序我这个对象需要序列化,那么真正的实现还要以来序列化流,比如写出到文件时,我们需要用到的ObjectOutputStream,它在序列化的时候会依次调用 writeObject()→writeObject0()→writeOrdinaryObject()→writeSerialData()→invokeWriteObject()→defaultWriteFields()。
然后最后一步的defaultWriteFields()方法中,会去调用ObjectStreamClass对象,里面有个方法为getDefaultSerialFields(),提供了可以被序列化的属性值。
private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
// 获取该类中声明的所有字段
Field[] clFields = cl.getDeclaredFields();
ArrayList<ObjectStreamField> list = new ArrayList<>();
int mask = Modifier.STATIC | Modifier.TRANSIENT;
// 遍历所有字段,将非 static 和 transient 的字段添加到 list 中
for (int i = 0; i < clFields.length; i++) {
Field field = clFields[i];
int mods = field.getModifiers();
if ((mods & mask) == 0) {
// 根据字段名、字段类型和字段是否可序列化创建一个 ObjectStreamField 对象
ObjectStreamField osf = new ObjectStreamField(field.getName(), field.getType(), !Serializable.class.isAssignableFrom(cl));
list.add(osf);
}
}
int size = list.size();
// 如果 list 为空,则返回一个空的 ObjectStreamField 数组,否则将 list 转换为 ObjectStreamField 数组并返回
return (size == 0) ? NO_FIELDS :
list.toArray(new ObjectStreamField[size]);
}
这段源码中,定义一个mask标记变量,用于接收访问修饰符中包含STATIC与TRANSIENT的属性,并在后面的if判断中,将这种mask的过滤掉,从而实现遍历所有字段,将非 static 和 transient 的字段添加到 list 中。
而这段源码就证明了,为什么在对象序列化过程中,static和transient不会被序列化!
四、总结
好啦,今天针对为什么static和transient关键字修饰的变量不能被序列化进行了一个解释,下次大家在面试的时候再被问道就可以这样回答啦,不过,还有的BT面试官会问transient关键字修饰的变量真的不能被序列化吗?这个问题咱们后面继续讨论哈。
面试官:告诉我为什么static和transient关键字修饰的变量不能被序列化?的更多相关文章
- static 和 final 关键字 对实例变量赋初始值的影响
static 和 final 关键字 对实例变量赋初始值的影响 最近一直在看<深入理解Java虚拟机>,在看完了对象内存分配.Class文件格式之后,想深扒一下实例变量是如何被赋上初始值的 ...
- 静态static关键字概述-静态static关键字修饰成员变量
静态static关键字概述 概述 关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属 于某个对象的.也就是说,既然属于类,就可以不靠创建对象来 ...
- BAT面试官告诉你如何回答你的职业规划
前言(Why) 在面试中不论是在一面二面三面这种技术面,还是在最后的hr面,经常会被人问及,"谈谈你的职业规划"这种问题,我们回答的很可能会给我们的面试表现加分,如果回答地不好,对 ...
- 谷歌PM面试官告诉你,如何成功拿到理想offer
快来看看大咖Nick如何甄选人才~文章转自墨刀公众号. 本文作者: Nick Baum,目前在谷歌做PM 作为谷歌的产品经理和面试官,我已经面过几百个PM的候选人了吧.这些经历能让我分享一下在PM求职 ...
- 阶段1 语言基础+高级_1-3-Java语言高级_1-常用API_1_第6节 static静态_12_静态static关键字修饰成员变量
创建一个学生类 定义成员变量,无参构造,全参构造.成员变量的getter和setter 所在教室必须是一样的,定义所在教室 下面来创建两个学生 只给one的room赋值了.two的教室并没有赋值.都输 ...
- 静态static关键字概述和静态static关键字修饰成员变量
static关键字 概述 关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属 于某个对象的.也就是说,既然属于类,就可以不靠创建对象来调用了 ...
- K:java序列化与反序列化—transient关键字的使用
首先,应该明白的是transient是java中的一个关键字,音标为 英: [ˈtrænziənt]. 在了解transient关键字之前,应该先弄明白序列化和反序列化.所谓的序列化,通俗点的 ...
- transient关键字和serialVersionUID
此文章很大部分转载于Java的架构师技术栈微信公众号,博主均测试通过加上自己理解写出 最近阅读java集合的源码,发现transient关键字,就了解了一下他的用法,transient关键字一般在实现 ...
- transient关键字的用法
本篇博客转自 一直在路上 Java transient关键字使用小记 1. transient的作用及使用方法 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,Java ...
- Java transient关键字使用小记
哎,虽然自己最熟的是Java,但很多Java基础知识都不知道,比如transient关键字以前都没用到过,所以不知道它的作用是什么,今天做笔试题时发现有一题是关于这个的,于是花个时间整理下transi ...
随机推荐
- dotnet 记 TaskCompletionSource 的 SetException 可能将异常记录到 UnobservedTaskException 的问题
本文将记录 dotnet 的一个已知问题,且是设计如此的问题.假定有一个 TaskCompletionSource 对象,此对象的 Task 没有被任何地方引用等待.在 TaskCompletionS ...
- games101-3 BRDF101
BRDF101 概述 本文基于知乎Maple对brdf的文章,在此基础又收集了一些其它来源的关于brdf的文章,希望能够完全理解记忆相关知识 关于Jakub Boksansky的文章,看的过程中又去搜 ...
- Java开启异步的两种方式
二.Java开启异步的两种方式 1.注解开启:@Async 1.1.配置异步的线程池 必须配置异步线程池,否则异步不会生效. @EnableAsync 注解:指定异步线程池.不指定默认使用:Simpl ...
- Servlet注解的使用,简化配置 以及,使用模板方法设计模式优化oa项目
Servlet注解的使用,简化配置 以及,使用模板方法设计模式优化oa项目 每博一文案 有句谚语说:"一怒之下踢石头,只有痛着脚趾头." 比一件糟糕的事情更可拍的,是你用糟糕的态度 ...
- 【保姆级Python入门教程】马哥手把手带你安装Python、安装Pycharm、环境配置教程
您好,我是 @马哥python说 ,一枚10年程序猿. 我的社群中小白越来越多,咨询讨论的问题很多集中在python安装上,故输出此文,希望对大家起步有帮助. 下面开始,先安装Python,再安装py ...
- vue3早已具备抛弃虚拟DOM的能力了
前言 jquery时代更新视图是直接对DOM进行操作,缺点是频繁操作真实 DOM,性能差.react和vue时代引入了虚拟DOM,更新视图是对新旧虚拟DOM树进行一层层的遍历比较,然后找出需要更新的D ...
- vue特殊attribute-key
官方说明:如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地复用相同类型元素的算法.而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在 ...
- 关于ESLint: Delete `␍`(prettier/prettier) 错误解决方案(3种)
1.点击VSCode右下角LF/CRLF然后根据弹窗修改成LF即可 2.在.eslintrc.cjs中增加配置 "rules": { "prettier/prettier ...
- pageoffice6实现Word在线套打
使用Word可以套打,其实套打一般就是将要打印的内容分毫不差的打印到已有的模板中去,比如奖状.证书.票据.报表等都可以使用套打完成. 方法一: 将Word页面排版的和打印纸中的页面完全相同,然后将打印 ...
- 在线电子表格编辑器 Luckysheet
下载地址 不知道地址正不正经,我先用着,网上找的地址,不知道是不是可信任的,疑似官网的两个地址都打不开, https://github.com/dream-num/Luckysheet zip包 h ...