Java对象序列化嵌套对象

5

当我学习Java中的序列化时,我遇到了一些问题:如何保存那些不可序列化且被类(实例变量)引用的对象的状态。在下面的代码中,我有一个类Dog(可序列化),它引用了一个类Collar(不可序列化);而这个类又引用了一个类Color(不可序列化)。尽管我尝试了所有可能性,但仍然出现错误。这是我最新编写的代码:

class Color {
    private String colorName;

    public String getColorName() {
        return colorName;
    }

    public void setColorName(String colorName) {
        this.colorName = colorName;
    }

    Color(String color) {
        this.colorName = color;
    }
}

class Collar {

    private Color color;
    private int size;

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public Color getColor() {
        return color;
    }

    public void setColor(Color color) {
        this.color = color;
    }

    Collar(int size, Color color) {
        this.size = size;
        this.color = color;
    }
}

class Dog implements Serializable {

    Dog(String breed, Collar collar) {
        this.breed = breed;
        this.collar = collar;
    }
    private String breed;

    public String getBreed() {
        return breed;
    }

    public void setBreed(String breed) {
        this.breed = breed;
    }

    public Collar getCollar() {
        return collar;
    }

    public void setCollar(Collar collar) {
        this.collar = collar;
    }
    transient private Collar collar;

    private void writeObject(ObjectOutputStream os) {
        try {
            os.defaultWriteObject();
            os.writeInt(this.getCollar().getSize());
            os.writeUTF(this.getCollar().getColor().getColorName());
            os.close();
        } catch (IOException ex) {
            Logger.getLogger(Dog.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void readObject(ObjectInputStream is) {
        try {
            is.defaultReadObject();
            int size = is.readInt();
            String colorName = is.readUTF();
            this.setCollar(new Collar(size, new Color(colorName)));
            is.close();
        } catch (Exception ex) {
            Logger.getLogger(Dog.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

public class App0001 {

    public static void main(String[] args) {

        try {
            Dog d = new Dog("Great Dane", new Collar(3, new Color("RED")));
            //System.out.println(d.getCollar().getColor().getColorName());
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("obj.ser"));
            os.writeObject(d);
            os.close();

            ObjectInputStream is = new ObjectInputStream(new FileInputStream("obj.ser"));
            d = (Dog) is.readObject();
            System.out.println(d.getCollar().getColor().getColorName());
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }
}

And I am getting following error:

java.io.IOException: Write error
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(FileOutputStream.java:260)
at java.io.ObjectOutputStream$BlockDataOutputStream.drain(ObjectOutputStream.java:1847)
at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(ObjectOutputStream.java:1756)
at java.io.ObjectOutputStream.writeNonProxyDesc(ObjectOutputStream.java:1257)
at java.io.ObjectOutputStream.writeClassDesc(ObjectOutputStream.java:1211)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1395)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
at java.io.ObjectOutputStream.writeFatalException(ObjectOutputStream.java:1547)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:333)
at Serialization.App0001.main(App0001.java:121)

这不是生产代码,仅用于练习和理解。


3
哦,谁给这个贴踩了?为什么?没有评论吗?实际上,我认为这个问题已经足够完整了!它包括了主题的简短描述、问题描述以及异常信息(包含堆栈跟踪)和代码,而这些代码只是为了解决这个问题而精简过的。很不错! - Seelenvirtuose
2个回答

2

readObjectwriteObject中,您不应关闭流!否则,下一次的写入/读取操作会失败。

通常来说,流(以及其他资源)应该按照以下方式处理:

  • 如果您的方法拥有该流,即您的方法打开了它 - 在同一个方法中关闭它(通常在try-with-resource语句中执行)。
  • 如果您的方法不拥有该流,即它从其他地方传递得到流(通常通过方法参数传递),则不要关闭它,因为您不知道流的所有者在您的方法返回后想要做什么。

什么是下一个读/写操作?我只写了两个字段,流的目的已经完成了。之后我不会再写任何其他内容。 - Amit Petkar
如果需要序列化多个对象怎么办?如果框架想要在之后进行一些额外的写入操作怎么办?由于您不拥有该流,因此请勿关闭它! - isnot2bad

1
在写入流时,如果流被关闭,则会出现IOException中的“写入错误”。
分析您的代码,我发现您的类Dog中有一个自定义的writeObject方法。在其中,您不能关闭流,因为它需要继续写入。所以只需删除该行即可。
os.close();

在你的writeObject方法中。哦,还要删除这行。
is.close();

在`readObject`方法中。
好的,我会更详细地解释一下。您在主方法中有以下代码:
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("obj.ser"));
os.writeObject(d);
os.close();

在这里,您正在创建流,使用它,然后关闭它。这是正确的关闭位置,因为这是流的负责位置。
想象一下,您有一个嵌套的可序列化对象结构,其类定义都包含自定义的writeObject方法。当调用ObjectOutputStream的writeObject方法时,它会通过调用每个对象的writeObject方法来遍历对象图。ObjectOutputStream控制写入顺序,并且它也自己编写控制字节。创建和关闭必须在外部完成(就像您已经做的那样)。

它起作用了。谢谢。然而,我仍然不明白为什么不应该关闭输入和输出流。你所说的继续写入是什么意思?我的意思是我正在写入两个字段,这就完成了目的,流应该关闭以节省资源。 - Amit Petkar
你不应该关闭输入和输出流,因为你不知道何时完成它们的使用。其他人会调用你的方法并传递这些参数:他们拥有这些流,而不是你。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接