transient 在 Serialization Java 中如何与 final 一起工作

How transient works with final in Serialization Java

我正在阅读有关 transient 和 final 关键字的内容,我找到了我们不能将 transient 关键字与 final 关键字一起使用的答案。我试过并感到困惑,因为它在这里工作正常。

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){/**/}
    }
}

这是可序列化的 Student class 代码:

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

没有 transient 关键字的输出。

ABC34
DEF64

你能解释一下为什么它工作正常吗?有错误吗?

最后,带有 final 关键字的 transient 行为应该是什么?

你的问题有点重复:

  • final-transient-fields-and-serialization
  • a-transient-final-field-used-as-a-lock-is-null

A final field must be initialized either by direct assignment of an initial value or in the constructor. During deserialization, neither of these are invoked, so initial values for transients must be set in the 'readObject()' private method that's invoked during deserialization. And for that to work, the transients must be non-final.

Any field that is declared transient is not serialized. Moreover, according to this blog post, field values are not even initialized to the values that would be set by a default constructor. This creates a challenge when a transient field is final.

关于您的测试结果:

Code output with transient keyword. ABC04 DEF04
Output without transient keyword. ABC34 DEF64

瞬态

显然,transient 字段(第 4 个字符)不是 serialized/deserialized (ABC34->ABC04 和 DEF64->DEF04)

静态

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

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

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

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

也许它会对你有所帮助,如果你添加间距甚至像
这样的标识符 "name="+name+", id="+id+""+", marks="+marks 在字符串表示中
toString().

返回

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

transient final int foo = 42;

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

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