为什么ObjectInput / OutputStream在读取和写入文件时会丢失对象引用?

问题描述 投票:-1回答:1

我有两个相互作用的类:

货架,存放CD,DVD,PaperMedias:

    public class Shelf {
        private ArrayList<CD> cds;
        private ArrayList<DVD> dvds;
        private ArrayList<PaperMedia> paperMedias;
          ...etc

和客户:

public class Customer implements Serializable {
    private Map<PhysicalMedia, Calendar> mediaOwned;  
    private Map<PhysicalMedia,Calendar> mediaReturned; 
    private Map<PhysicalMedia,CalendarPeriod> mediaOnHold;
     ...etc

物理媒体是CD,DVD,PaperMedia的父母。

首先,我将使用一些物品初始化货架,并且客户借用一些这些物品。然后我将这些对象保存到ShelfObjects.txt和CustomerObjects.txt。

当我再次从这些文件中读取这些对象时,似乎两者之间的链接丢失了,特别是在客户的PhysicalMedia和架子的PhysicalMedia之间。这些应该具有相同的参考,例如Metallica CD应该在Shelf中具有与Customer帐户中相同的引用。

因此,当我更改说Metallica CD的状态时,它不会在其他来源中更新它!

有没有保留这个参考?

我在CustomerDatabase类中以下列方式加载和保存媒体:

public class CustomersDatabase implements Serializable {

    private ArrayList<Customer> customers = new ArrayList<Customer>();
       //etc..

public void load() {
        try {
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("CustomersObject.txt"));

            try {
                customers.clear();
                while(true) {
                    customers.add((Customer)in.readObject());
                }
            } catch (ClassNotFoundException e) {
                System.out.println("Customer class in file wasn't found");

            }

            in.close();

        } catch (FileNotFoundException e) {
            System.out.println("File not found");
            e.printStackTrace();
        } catch (IOException e) {

            System.out.println("\n^ Customer load successful\n");


        }

public void save() {

    try {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("CustomersObject.txt",false));
        for (int i=0;i<customers.size();i++) {
            out.writeObject(customers.get(i));
            }


        out.close();

        System.out.println("\nCustomer save successful\n");

    } catch (FileNotFoundException e) {
        System.out.println("File not found");
        e.printStackTrace();
    } catch (IOException e) {
        System.out.println("IO exception ");
        e.printStackTrace();
    }

}

我在Shelf类中做类似的加载和存储。

java io stream objectinputstream objectoutputstream
1个回答
2
投票

问题是您使用两个单独的对象序列化流使用两个单独的文件存储两个数据库。

您当前的方法不适合您的原因是对象序列化协议将对象引用存储为简单数字。它将流中的对象编号为1,2,3等,并使用这些数字来引用流中先前序列化的对象。

这适用于一个流的上下文。但是当您有两个或更多个流时,给定对象可能在每个流中具有不同的数字。这意味着readObject方法无法知道两个不同流中的两个对象实际上应该链接在一起。

简单的解决方案就是做这样的事情:

ObjectInputStream in = new ObjectInputStream(new  FileInputStream("DB.txt"));
for (int i = 0; i < customers.size(); i++) {
    out.writeObject(customers.get(i));
}
for (int i = 0; i < shelves.size(); i++) {
    out.writeObject(shelves.get(i));
}

或者更好的是,直接序列化customersshelves列表,因为这将简化反序列化。

(是的......这会弄乱你当前的代码结构。你必须在同一个地方加载并保存CustomerShelf数据库,或者在这个地方传递一个开放的流。)

也可以将对象存储在两个单独的文件中,但您需要使用自定义序列化方法来执行此操作。

  • 首先,您需要确定您希望每个共享子对象进入哪个文件;即PhysicalMedia对象是否存储在Customer数据库或Shelf数据库中?
  • 对于每个共享对象类,定义适当类型的标识符字段;例如String或`long。修改您的代码库,以便使用在应用程序上下文中唯一的值填充该字段。
  • 对于要在其中生成对象的数据库,请按正常顺序进行序列化。
  • 对于其他数据库,请使用对象序列化高级功能: 在写入时,用(仅)标识符替换对象,和 在读取时,查找第一个数据库中的标识符,然后将查找的对象插入到反序列化的对象中。

最后一项需要深入了解对象序列化API,自定义序列化等。在您的示例中,您要增加的复杂性是要替换的对象是Map对象中的键。由于您无法为标准地图类添加自定义readObjectwriteObject,因此您需要在Stream类本身的自定义子类中执行深层技巧。 (而且......不,我不能举个例子!)


旁白:这种事情是使用对象序列化来实现数据库的不可取的原因之一。最好使用真正的数据库和ORM技术,如JPA(例如Hibernate)。还有其他原因。

© www.soinside.com 2019 - 2024. All rights reserved.