transient如何在Java中与final序列化一起工作

transient如何在Java中与final序列化一起工作,java,serialization,java-8,final,transient,Java,Serialization,Java 8,Final,Transient,我读了一些关于transient和final关键字的文章,我发现答案是我们不能将transient关键字与final关键字一起使用。我试过了,但弄糊涂了,因为它在这里工作得很好 import java.io.FileOutputStream; import java.io.FileInputStream; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.Serializable

我读了一些关于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){/**/}
    }
}
以下是可序列化的学生班级代码:

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
不带瞬态关键字的输出

ABC04
DEF04
ABC34
DEF64
你能解释一下为什么它工作得很好吗?有虫子吗


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

您的问题有点重复:

最后一个字段必须通过直接赋值或在构造函数中初始化。在反序列化过程中,这两个函数都不会被调用,因此必须在反序列化过程中调用的“readObject()”私有方法中设置瞬态的初始值。要使其工作,瞬变必须是非最终的

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

至于你的测试结果: 带有transient关键字的代码输出。 ABC04 DEF04
不带瞬态关键字的输出。 ABC34 DEF64

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

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

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

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

  • 字段
    name
    不是
    transient
    ,因此正确存储、还原和打印
  • 字段
    标记
    被声明为
    静态
    ,因此不属于对象状态的一部分,也不会存储或恢复。它始终显示最后写入的值,
    4
    ,尽管第一个对象已向其写入
    2
    ,因为第二个对象甚至在测试开始之前就用
    4
    覆盖它
  • 只有字段
    id
    是未存储的
    transient
    实例字段,因此显示默认值
    0
    。删除
    transient
    关键字时,将存储并还原该关键字,第一个对象显示
    3
    ,第二个对象显示
    6
  • 如果您添加间距或标识符(如
    “name=“+name+”,id=“+id+”+,marks=“+marks
    在字符串表示法中
    toString()
    返回

    添加另一个角盒,如果添加字段,则会产生违反直觉的行为

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

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