使用ArrayList字段对类的Singleton实例进行序列化和反序列化的问题

时间:2019-01-16 08:01:27

标签: java serialization singleton deserialization

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

这是我的Serializer类的代码:

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对象,情况并非如此。

创建新帐户后,实例及其大概关联的字段(即 accounts 列表)将被序列化。

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;
}

帐户和消息均实现可序列化,并且都具有serialVersionUID。我没有任何不可序列化的异常。

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

1 个答案:

答案 0 :(得分:0)

我设法解决了我的问题。我的问题出在我申请的另一部分。本质上,我认为MessageManager不会被序列化,因为我无法从中获取期望的数据。事实证明,问题出在返回数据的方法中:我正在使用对象引用来标识要返回的数据。当然,反序列化会创建新的实例,因此,在重新创建对象之后,我将无法再访问由旧实例标识的数据。

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

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