经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
面试官:告诉我为什么static和transient关键字修饰的变量不能被序列化?
来源:cnblogs  作者:JavaBuild  时间:2024/6/25 8:54:49  对本文有异议

一、写在开头

在上一篇学习序列化的文章中我们提出了这样的一个问题:

“如果在我的对象中,有些变量并不想被序列化应该怎么办呢?”

当时给的回答是:不想被序列化的变量我们可以使用transientstatic关键字修饰;transient 关键字的作用是阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复;而static关键字修饰的变量并不属于对象本身,所以也同样不会被序列化!

当时没有解释具体为什么static和transient 关键字修饰的变量就不能被序列化了,这个问题实际上在很多大厂的面试中都可能会被问及。我们今天在这篇中进行解释吧。

二、案例演示

我们先通过一个实战案例,去看一看用static和transient 关键字修饰后的变量,序列化与反序列化后的现象。

  1. public class TestService {
  2. public static void main(String[] args) throws IOException {
  3. //初始化对象信息
  4. Person person = new Person();
  5. person.setName("JavaBuild");
  6. person.setAge(30);
  7. System.out.println(person.getName()+" "+person.getAge());
  8. //序列化过程
  9. try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("E:\\person.txt"));) {
  10. objectOutputStream.writeObject(person);
  11. } catch (IOException e) {
  12. e.printStackTrace();
  13. }
  14. person.par1 = "序列化后静态字段";
  15. //反序列化过程
  16. try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("E:\\person.txt"));) {
  17. Person p = (Person) objectInputStream.readObject();
  18. System.out.println(p);
  19. } catch (IOException | ClassNotFoundException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. }
  24. class Person implements Serializable{
  25. private static final long serialVersionUID = 8711922740433840551L;
  26. private String name;
  27. private int age;
  28. public static String par1 = "静态字段";
  29. transient String par2 = "临时字段";
  30. transient int high = 175;
  31. public String getName() {
  32. return name;
  33. }
  34. public void setName(String name) {
  35. this.name = name;
  36. }
  37. public int getAge() {
  38. return age;
  39. }
  40. public void setAge(int age) {
  41. this.age = age;
  42. }
  43. @Override
  44. public String toString() {
  45. return "Person{" +
  46. "name='" + name + '\'' +
  47. ", age=" + age +
  48. ", par1=" + par1 +
  49. ", high=" + high +
  50. ", par2='" + par2 + '\'' +
  51. '}';
  52. }
  53. }

在Person类中,我们定义了两个正常的属性,姓名与年龄,同时呢,我们也分别定义了一个静态字段和两个临时字段,输出结果为:

  1. JavaBuild 30
  2. 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(),提供了可以被序列化的属性值。

  1. private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
  2. // 获取该类中声明的所有字段
  3. Field[] clFields = cl.getDeclaredFields();
  4. ArrayList<ObjectStreamField> list = new ArrayList<>();
  5. int mask = Modifier.STATIC | Modifier.TRANSIENT;
  6. // 遍历所有字段,将非 static 和 transient 的字段添加到 list 中
  7. for (int i = 0; i < clFields.length; i++) {
  8. Field field = clFields[i];
  9. int mods = field.getModifiers();
  10. if ((mods & mask) == 0) {
  11. // 根据字段名、字段类型和字段是否可序列化创建一个 ObjectStreamField 对象
  12. ObjectStreamField osf = new ObjectStreamField(field.getName(), field.getType(), !Serializable.class.isAssignableFrom(cl));
  13. list.add(osf);
  14. }
  15. }
  16. int size = list.size();
  17. // 如果 list 为空,则返回一个空的 ObjectStreamField 数组,否则将 list 转换为 ObjectStreamField 数组并返回
  18. return (size == 0) ? NO_FIELDS :
  19. list.toArray(new ObjectStreamField[size]);
  20. }

这段源码中,定义一个mask标记变量,用于接收访问修饰符中包含STATIC与TRANSIENT的属性,并在后面的if判断中,将这种mask的过滤掉,从而实现遍历所有字段,将非 static 和 transient 的字段添加到 list 中。

而这段源码就证明了,为什么在对象序列化过程中,static和transient不会被序列化!

四、总结

好啦,今天针对为什么static和transient关键字修饰的变量不能被序列化进行了一个解释,下次大家在面试的时候再被问道就可以这样回答啦,不过,还有的BT面试官会问transient关键字修饰的变量真的不能被序列化吗?这个问题咱们后面继续讨论哈。

原文链接:https://www.cnblogs.com/JavaBuild/p/18263716

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号