首页 > 解决方案 > 序列化和反序列化具有 ArrayList 字段的类的单例实例的问题

问题描述

目前,我正在编写一个使用单例模式和序列化的 Java 应用程序。我有一个专用的序列化程序类,可以对给定文件路径的对象进行序列化和反序列化。我的一个对象被序列化和反序列化没有问题:我在应用程序打开时对其状态进行了一些更改,然后关闭应用程序,当我重新打开它时这些更改仍然存在。但是,这不适用于我的另一个对象,即使据我所知,它们之间没有重大区别应该会导致这种情况。

这是我的序列化器类的代码:

public static Boolean serialise(Object target, String filePath){
    try (FileOutputStream fileOut = new FileOutputStream(filePath);
        ObjectOutputStream objectOut = new ObjectOutputStream(fileOut);){

        objectOut.writeObject(target);
        return true;
    } catch (FileNotFoundException ex) {
        Logger.getLogger(Serialiser.class.getName()).log(Level.SEVERE, null, ex);
        return false;
    } catch (IOException ex) {
        Logger.getLogger(Serialiser.class.getName()).log(Level.SEVERE, null, ex);
        return false;
    }
}

public static Object deserialise(String filePath){
    try (FileInputStream fileIn = new FileInputStream(filePath);
        ObjectInputStream objectIn = new ObjectInputStream(fileIn);){
        Object readObject = objectIn.readObject();
        return readObject;
    } catch (IOException | ClassNotFoundException ex) {
        Logger.getLogger(Serialiser.class.getName()).log(Level.SEVERE, null, ex);
        return null;
    }
}

这是我的对象的相关代码。首先,一个有效的:

public class AccountManager implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final String FILE_PATH = "data//account_manager.ser";

    private static AccountManager instance;
    private ArrayList<Account> accounts = null;

    private AccountManager(){
        accounts = new ArrayList<>();
    }

    public static AccountManager getInstance(){
        if (instance == null){
            instance = (AccountManager)Serialiser.deserialise(FILE_PATH);
            if (instance == null){
                instance = new AccountManager();
                Serialiser.serialise(instance, FILE_PATH);
            }
        }
        return instance;
    }

现在不起作用的那个:

public class MessageManager implements Serializable {

private static final long serialVersionUID = 1L;
private static final String FILE_PATH = "data//message_manager.ser";

private static MessageManager instance = null;
private ArrayList<Message> messages = null;

private MessageManager(){
    messages = new ArrayList<>();
}

public static MessageManager getInstance(){
    if (instance == null){
        instance = (MessageManager)Serialiser.deserialise(FILE_PATH);
        if (instance == null){
            instance = new MessageManager();
            Serialiser.serialise(instance, FILE_PATH);
        }
    }
    return instance;
}

本质上,这两个类的工作方式相同:它们存储特定类型的对象列表,并提供对其各自列表内容的访问和执行操作。当访问类的 Singleton 实例时,它会检查是否可以从文件中反序列化实例。如果不能,它会实例化一个新的并将其序列化。同样,这适用于其中一个 AccountManager,但不适用于另一个 MessageManager。也就是说,如果在应用程序运行时创建新的 Account 对象并使用 AccountManager 存储它,如果我重新启动应用程序,它仍然会存在。对于 MessageManager 和 Message 对象,情况并非如此。

当一个新的帐户被创建时,该实例以及可能与它相关的字段,即帐户列表被序列化。

public Account createAccount(String password, String givenName, String surname, String address, Gender gender, LocalDate dateOfBirth){
        String id = IDGenerator.getInstance().generateID(AccountType.PATIENT, accounts);
        Account createdAccount = null;

        if (id != null){
            createdAccount = new PatientAccount(id, password, givenName, surname, address, gender, dateOfBirth);
            if (createdAccount != null){
                accounts.add(createdAccount);
                Serialiser.serialise(instance, FILE_PATH);
            }
            return createdAccount;
        }
        return null;
    }

在 MessageManager 中,当一个新的 Message 实例被添加到它的列表时,该实例以及它的列表再次被序列化:

public Boolean sendMessage(Message message){
    if (messages.add(message)){
        Serialiser.serialise(instance, FILE_PATH);
        return true;
    }
    return false;
}

Account 和 Message 都实现了 Serializable,并且都有 serialVersionUID。我没有得到任何 NotSerializable 异常。

任何有助于解决此问题的帮助将不胜感激。如有必要,我可以提供更多我的应用程序代码。

标签: javaserializationsingletondeserialization

解决方案


我设法解决了我的问题。我的问题在我的应用程序的不同部分。本质上,我相信 MessageManager 没有被序列化,因为我无法从它那里得到我期望的数据。事实证明,问题出在返回数据的方法中:我使用对象引用来识别要返回的数据。当然,反序列化会创建新实例,因此,在重新创建对象后,我无法再访问旧实例标识的数据。

为了解决我的问题,我使用了另一种识别数据的方法:一个字符串属性,其值在反序列化之前和之后的实例之间是相同的。

感谢所有看过我的问题和/或试图回答的人。


推荐阅读