【问题标题】:How transient works with final in Serialization Java在序列化 Java 中瞬态如何与 final 一起工作
【发布时间】:2016-10-03 12:04:17
【问题描述】:

我正在阅读有关瞬态和最终关键字的信息,我找到了我们不能将瞬态关键字与最终关键字一起使用的答案。我试过但很困惑,因为它在这里工作正常。

import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class SerExample{
    public static void main(String... args){
        Student foo = new Student(3,2,"ABC");
        Student koo = new Student(6,4,"DEF");
        try
        {
            FileOutputStream fos = new FileOutputStream("abc.txt");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(foo);
            oos.writeObject(koo);
            oos.close();
            fos.close();
        }
        catch(Exception e){/**/}

        try{
            FileInputStream fis = new FileInputStream("abc.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            System.out.println(ois.readObject());
            System.out.println(ois.readObject());
            fis.close();
            ois.close();
        }catch(Exception e){/**/}
    }
}

这是可序列化的学生类代码:

class Student implements Serializable{
        private transient final int id;
        private transient static int marks;
        private String name;
        public Student(int id, int marks, String name){
            this.id = id;
            this.marks = marks;
            this.name = name;
        }
        public Student(){
            id=0;
        }
        @Override
        public String toString(){
            return (this.name + this.id + this.marks);
        }
    }

带有transient关键字的代码输出。

ABC04
DEF04

不带瞬态关键字的输出。

ABC34
DEF64

您能解释一下为什么它运行良好吗?有错误吗?

最后,使用 final 关键字的瞬态行为应该是什么?

【问题讨论】:

    标签: java serialization java-8 final transient


    【解决方案1】:

    你的问题有点重复:

    必须通过直接分配初始值或在构造函数中初始化最终字段。在反序列化期间,它们都不会被调用,因此必须在反序列化期间调用的“readObject()”私有方法中设置瞬态的初始值。而要让它发挥作用,瞬态必须是非最终的。

    任何声明为瞬态的字段都不会序列化。此外,根据这篇博客文章,字段值甚至没有初始化为默认构造函数设置的值。当瞬态字段为最终字段时,这会带来挑战。

    至于你的测试结果:

    带有瞬态关键字的代码输出。 ABC04 DEF04
    不带瞬态关键字的输出。 ABC34 DEF64

    瞬态

    显然,transient 字段(第 4 个字符)没有被序列化/反序列化(ABC34->ABC04 和 DEF64->DEF04)

    静态

    static 字段(第 5 个字符)也没有被反序列化!这仅仅是因为您在相同的内存空间中执行操作,并且静态字段保留在所有实例中。所以当你在student上设置静态字段,然后反序列化另一个student时,静态字段当然还是一样的值!

    这也解释了为什么在您的测试中首先将静态字段设置为 2 和 然后4,但只有4 被打印出来。在这种情况下,与序列化无关,只是静态字段行为。

    【讨论】:

      【解决方案2】:

      您认为您的示例有效的结论是错误的。

      1. 字段name 不是transient,因此正确存储、恢复和打印。
      2. 字段marks 被声明为static,因此不是对象状态的一部分,并且从未存储或恢复。尽管您的第一个对象已将 2 写入其中,但它始终显示最后写入的值 4,因为在您的测试开始之前,第二个对象已用 4 覆盖它。
      3. 只有字段idtransient实例字段,不存储,因此显示默认值0。删除 transient 关键字时,它会被存储和恢复,第一个对象显示 3,第二个对象显示 6

      如果您在字符串表示中添加空格或什至像
      "name="+name+", id="+id+""+", marks="+marks 这样的标识符
      ,也许它会对您有所帮助 由toString()返回。

      添加另一个极端情况,如果添加字段,则会产生违反直觉的行为

      transient final int foo = 42;
      

      对于你的类,你也会体验到它在恢复后显示正确的值,因为它是一个编译时常量。因此,任何引用此变量的代码都将始终使用常量值并且从不实际读取实例字段,因此它没有被恢复的事实不会被注意到。但是,当然,最好将这样的常量声明为 static,以避免为从未读取的实例字段浪费内存。

      另一个可能令人惊讶的例子是在enum 中声明transient final 字段。它们似乎总是显示正确的值,因为 enum 对象的状态永远不会被存储,但是当反序列化 enum 值时,已经初始化的 enum 类型的实际常量对象被解析。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-03-05
        • 1970-01-01
        • 1970-01-01
        • 2012-03-30
        • 2010-12-27
        • 1970-01-01
        • 2015-10-18
        • 1970-01-01
        相关资源
        最近更新 更多