【问题标题】:Java deserialization: final variable value is not assignedJava反序列化:未分配最终变量值
【发布时间】:2015-09-15 14:32:06
【问题描述】:

我正在探索 Java 序列化和反序列化,但想知道反序列化是如何工作的。

这是我的序列化和反序列化代码:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Testing{
    public static void main(String[] args){
        Serial obj = new Serial();
        Serial ObjNew = null;
        try{
           FileOutputStream fob= new FileOutputStream("file.src"); 
           ObjectOutputStream oob= new ObjectOutputStream(fob);
           oob.writeObject(obj);
           oob.close();
        }catch(Exception fnf){
            fnf.printStackTrace(System.out);
        }
        try{
            FileInputStream fis = new FileInputStream("file.src");
            ObjectInputStream ois = new ObjectInputStream(fis);
            ObjNew=(Serial)ois.readObject();
        }
        catch(IOException | ClassNotFoundException fnf){
            fnf.printStackTrace(System.out);
        }
        System.out.println("OLD: "+obj);
        System.out.println("NEW: "+ObjNew);

    }
}

这里是可序列化的类代码:

import java.io.Serializable;
public class Serial implements Serializable {
    private final String ok="DONE";
    @Override
    public String toString(){
        return ok;
    }
}

上面的代码一切正常。 但是当我尝试使用此代码反序列化对象时。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Testing{
    public static void main(String[] args){
        Serial ObjNew = null;
        try{
            FileInputStream fis = new FileInputStream("file.src");
            ObjectInputStream ois = new ObjectInputStream(fis);
            ObjNew=(Serial)ois.readObject();
        }
        catch(IOException | ClassNotFoundException fnf){
            fnf.printStackTrace(System.out);
        }
        System.out.println("NEW: "+ObjNew);

    }
}

我只是在 Serializable 类中更改了 String ok="Do"; 的值,只是为了测试反序列化输出。

但它只是打印Do,而不是存储在文件中的值DONE。 为什么它只打印Do为什么它不从文件中读取值? 有什么帮助吗?

【问题讨论】:

    标签: java serialization


    【解决方案1】:

    对可序列化的 Java 类的要求是它可以访问其第一个不可序列化超类中的无参数构造函数。 Java 序列化利用这个无参数构造函数。这与声明它通过此构造函数创建一个新类并不完全相同,但类构造的某些方面是在类中设置字段之前执行的。这些方面之一是最终常量的设置,因为需要在类被视为“构造”之前设置它们。

    你有一个 final 字段。类构建后不能设置。有了这样一个项目,该字段不会序列化到磁盘上的文件中(至少我不认为它是),即使它曾经是并且是从磁盘读取的,它也无法重置该字段,因为那样该字段就不会兑现“最终”关键字。如果您将其更改为非最终版本,您应该会得到不同的结果。

    由于您实际上是在借用一个类定义并使用该定义的快照填充它,因此您需要一种工具来识别定义何时偏离得太远而无法协调。这是serialVersionID 成员的主要用途。

    【讨论】:

    • "Java 序列化使用无参数构造函数。" 不完全是,在这种情况下,如果没有无参数构造函数,我们将无法反序列化对象。据我记得它使用不涉及构造函数的机制(Unsafe...Something 类)。
    • 对可序列化的 Java 类的要求是它可以访问其第一个不可序列化超类中的无参数构造函数。我们可能会质疑“利用”的含义,但如果您可以创建一个不使用无参数构造函数的可序列化类,我希望看到它。请注意,我没有说它需要一个构造函数来反序列化,也没有说它是通过运行没有 args 的“init”方法构造的......
    • 你是对的。但是您能否将此解释添加到您的答案中以避免混淆?
    • 更新(快速)。如果您觉得快速编辑不愉快,请随时提出建议和编辑。
    • 看起来不错。感谢您改进了您的帖子并教会了我一些新东西:)
    【解决方案2】:

    final 变量只能赋值一次。

    删除 final 修饰符,然后重试。

    【讨论】:

    • 非常感谢解决了这个问题。
    • 那是因为...private final String ok="DONE"; 行添加了一个类型为 String 的字段“ok”,带有修饰符 privatefinal 到封闭类,并像 @987654327 这样的赋值@进入instance initializer。并不是说常量总是可能被编译器替换。
    【解决方案3】:

    这取决于您的 final 字段修饰符。

    我红了ObjectInputStream的源码,找到了这段代码

        Object obj;
        try {
            obj = desc.isInstantiable() ? desc.newInstance() : null; // here instance created
        } catch (Exception ex) {
            throw (IOException) new InvalidClassException(
                desc.forClass().getName(),
                "unable to create instance").initCause(ex);
        }
    

    一旦您的实例创建了应用于该字段的值 “DO”,并且由于字段为 final,它就不能再修改了

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-22
      • 1970-01-01
      • 2016-08-28
      • 1970-01-01
      相关资源
      最近更新 更多