【问题标题】:Tricky situation with serializing and deserializing transient objects序列化和反序列化瞬态对象的棘手情况
【发布时间】:2012-10-04 15:00:32
【问题描述】:

这是我的代码:

class Collar {
  int size;
}

class Dog implements Serializable {
  int weight;
  transient Collar c;

  public Dog(int weight, int size) {
    this.weight = weight;
    c = new Collar();
    c.size = size;
  }

  public void writeObject(ObjectOutputStream os) throws Exception {
    os.defaultWriteObject();
    os.writeInt(c.size);
  }

  public void readObject(ObjectInputStream is) throws Exception {
    is.defaultReadObject();
    c = new Collar();
    c.size = is.readInt();
  }
}

public class test {
  public static final String FILE_REVISION = "$Revision$";

  public static void main(String ar[]) throws Exception {
    Dog dOut = new Dog(20, 2);

    System.out.println("DOut Weight: " + dOut.weight + " Size: " + dOut.c.size);

    FileOutputStream fo = new FileOutputStream("Dog.ser");
    ObjectOutputStream os = new ObjectOutputStream(fo);
    os.writeObject(dOut);

    FileInputStream fi = new FileInputStream("Dog.ser");
    ObjectInputStream is = new ObjectInputStream(fi);
    Dog dIn = (Dog) is.readObject();

    System.out.println("DIn Weight: " + dIn.weight + " Size: " + dIn.c.size);
  }
}

这是输出:

DOut Weight: 20 Size: 2
Exception in thread "main" java.lang.NullPointerException
    at test.main(test.java:55)

第 55 行是包含 System.out.println() 的最后一行代码

程序能够取回可序列化的对象,这里是Dog。但是,当我使用自定义 readObject 方法回读时,它无法创建新的“包含”对象 Collar。我哪里错了?

如您所见,'dIn.c.size' 语句中的输出为 NullPointerException。尽管我在自定义 readObject 方法中将 c 设置为新对象,但它并没有真正起作用。

【问题讨论】:

    标签: java serialization transient


    【解决方案1】:

    因为Collar ctransientreadObject里面,所以你需要先初始化c

    c.size = is.readInt();<-- NullPointerException Here
    

    这里需要先初始化C

    c = new Collar();
    c.size = is.readInt();
    

    两个方法签名都是错误的,它们应该如下所示:

     private void writeObject(ObjectOutputStream stream) throws IOException {
        stream.defaultWriteObject();
        stream.writeInt(c.size);
    }
    
    private void readObject(ObjectInputStream stream) throws IOException,
            ClassNotFoundException {
        stream.defaultReadObject();
        c = new Collar();
        c.size = stream.readInt();
    }
    

    您可以阅读有关序列化的更多详细信息here

    【讨论】:

    • 你的意思是我应该在类中声明对象时进行初始化?我认为这是一个糟糕的设计。没有其他解决方法吗?这个例子取自 Kathy 的 SCJP 书。但它不起作用。
    • 你需要了解序列化是如何工作的,这不是一个糟糕的设计。由于此变量在 readObject 中是瞬态的,因此您需要执行与构造函数中相同的操作。
    • 我的错。我以为您在谈论在声明 c 时对其进行初始化。所以,你和其他用户的建议,我已经做到了。不小心在复制之前我做了太多次“撤消”并错过了那行。即使我在为它的成员分配在 readObject 中读回的值之前初始化了 c,它也不起作用。主要问题已更新,谢谢。
    • 完美!是的,它们必须是私人的,而不是公开的。非常感谢。我正在尝试为您的答案投票,但我是新用户,因此不允许。您的回答解决了这个问题。
    • AmitD 是对的:问题是方法的签名是错误的,所以它们根本没有被调用。就其价值而言,关于签名的重要事项是名称、参数类型和可见性,这些都必须受到保护。 throws 列表实际上似乎并不重要。
    【解决方案2】:

    readObject 方法中初始化您的Collar 成员

    public void readObject(ObjectInputStream is) throws Exception {
        is.defaultReadObject();
        c = new Collar();
        c.size = is.readInt();
    }
    

    编辑: Serializable 方法需要更改为私有。

    【讨论】:

    • 对不起我的错。在复制代码之前,我意外地撤消了。所以,不,即使那样也行不通。我将用您的建议更新主要问题。谢谢。
    • 不,它没有。这就是让我觉得这是一个棘手的情况的原因。代码已准备好被复制和编译以签入您的机器(您只需要添加导入语句)。
    猜你喜欢
    • 1970-01-01
    • 2015-10-18
    • 1970-01-01
    • 2021-04-22
    • 1970-01-01
    • 1970-01-01
    • 2010-12-27
    • 2016-10-14
    • 2014-12-24
    相关资源
    最近更新 更多