【问题标题】:Why does ObjectInput/OutputStream lose object references when reading and writing to file?为什么 ObjectInput/OutputStream 在读写文件时会丢失对象引用?
【发布时间】:2019-07-31 16:11:38
【问题描述】:

我有两个交互的类:

搁板,用于存放 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 在货架上的引用应该与客户帐户中的引用相同。

所以当我更改 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 类中进行类似的加载和存储。

【问题讨论】:

  • 添加用于保存和读取文件数据的方法。
  • 标题中的松散是错字,应该是丢失的。
  • 我添加了加载和存储这些文件的方式
  • 如果您将Shelf 对象和Customer 对象序列化到文件中,它将起作用。反序列化创建了一组全新的对象。注意您不需要单独序列化列表的元素。你可以序列化列表。并且序列化数据不是文本,不应该保存在扩展名为 .txt 的文件中。
  • 所以你应该把它实现Serializable,这样你可以将它序列化到文件中。

标签: java io stream objectinputstream objectoutputstream


【解决方案1】:

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

您当前的方法对您不起作用的原因是对象序列化协议将对象引用存储为简单数字。它将流中的对象编号为 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 或`长。修改您的代码库,以便使用在应用程序上下文中唯一的值填充该字段。

  • 对于您希望对象存在的数据库,正常序列化。

  • 对于其他数据库,使用对象序列化高级功能:

    • 在写入时,将对象替换为(仅)其标识符,并且
    • 读取时,在第一个数据库中查找标识符,然后将查找到的对象插入到反序列化对象中。

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


顺便说一句:这种事情是不建议使用对象序列化来实现数据库的原因之一。最好使用真实的数据库,以及 JPA 等 ORM 技术(例如 Hibernate)。还有其他原因。

【讨论】:

  • 谢谢,这真的很有帮助。我会以这种方式修改我的代码。一整天都在努力解决这个问题。数据库听起来确实很棒,但这只是我试图制作的原型系统,所以不需要这样的东西
猜你喜欢
  • 2017-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多