序列化Java中的最终瞬态如何工作

时间:2016-06-03 12:11:51

标签: java serialization java-8 final transient

我正在阅读有关transient和final关键字的内容,我发现答案是我们不能使用带有final关键字的transient关键字。我试过并感到困惑,因为它在这里工作正常。

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

这是Serializable Student类代码:

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关键字进行瞬态行为?

2 个答案:

答案 0 :(得分:6)

您的问题与此有些重复:

  

必须通过直接赋值初始值或在构造函数中初始化final字段。在反序列化期间,这些都不会被调用,因此必须在' readObject()'中设置瞬态的初始值。在反序列化期间调用的私有方法。为了实现这一目标,瞬态必须是非最终的。

  

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

至于测试结果:

  

带有transient关键字的代码输出。   ABC04 DEF04
  输出没有临时关键字。   ABC34 DEF64

瞬态

显然,Session.Abandon() 字段(第4个字符)未被序列化/反序列化(ABC 3 4-> ABC 0 4且DEF 6 4-> DEF的 0 4)

静态

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

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

答案 1 :(得分:2)

你的例子有效的结论是错误的。

  1. 字段name不是transient,因此正确存储,恢复和打印。
  2. 字段marks声明为static,因此不属于对象的状态,也从不存储或恢复。它始终显示最后写入的值4,尽管您的第一个对象已向其写入2,因为第二个对象会在您的测试开始之前用4覆盖它。
  3. 只有字段id是未存储的transient实例字段,因此显示默认值0。删除transient关键字后,系统会对其进行存储和恢复,第一个显示3,第二个对象显示6
  4. 如果你添加间距甚至是像
    这样的标识符,也许它会对你有所帮助  字符串表示中的"name="+name+", id="+id+""+", marks="+markstoString()返回。

    如果添加字段

    ,则添加另一个案例,产生反直觉行为
    transient final int foo = 42;
    

    到您的课程,您还将体验它在恢复后显示正确的值,因为它是一个编译时常量。因此,引用此变量的任何代码都将始终使用常量值,并且永远不会实际读取实例字段,因此未恢复的事实不会被忽视。但是,当然,这样的常量更好地声明为static,以避免为永不读取的实例字段浪费内存。

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