Хранение и чтение объектов, содержащих другие объекты в Java с ObjectOutputStream ObjectInputStream - PullRequest
0 голосов
/ 24 апреля 2020

У меня есть класс Person и класс BankAccount следующим образом (извините, код на испанском sh):

package aplicacion.banco;

import java.io.Serializable;

public class Persona implements Serializable {

    String nif; // se asume unico para cada Persona
    String nombre; // nombre
    String apellidos; // apellidos

    public Persona(String nif, String nombre, String apellidos) {
        this.nif = nif;
        this.nombre = nombre;
        this.apellidos = apellidos;
    }
    // bunch of methods including setters and getters
}
package aplicacion.banco;

import java.io.Serializable;

public class CuentaBancaria implements Serializable {

    int numeroCuenta; // se asume unico para cada CuentaBancaria/CuentaBancariaVip
    Persona persona; // objeto Persona al que pertenece esta Cuenta
    double saldo; // saldo disponible

    public CuentaBancaria(int numeroCuenta, Persona persona, double saldo) {
        this.numeroCuenta = numeroCuenta;
        this.persona = persona;
        this.saldo = saldo;
    }

    public CuentaBancaria(int numeroCuenta, Persona persona) {
        this(numeroCuenta, persona, 0d);
    }
    // bunch of methods including setters and getters
}

Как видите, в классе BankAccount есть Person поле, в котором указано лицо, которому оно принадлежит. Таким образом, у меня может быть много учетных записей BankAccount, связанных с одним и тем же лицом, поэтому я могу получить все учетные записи BankAccount, связанные с одним лицом, и использовать их как единое целое, и это прекрасно работает. Тем не менее, у меня есть небольшая проблема при сохранении данных Person и BankAccount в файлы и последующем чтении их при повторном запуске программы ...

Следующий класс - это тот, который читает и записывает данные. Важными методами здесь являются cargarArchivos (), который загружает содержимое файлов в ArrayLists, actualizarArchivoPersonas (), который обновляет файл Person, и actualizarArchivoCuentas (), который делает то же самое для BankAccounts. Эти методы обновления вызываются всякий раз, когда я вносю изменения в ArrayLists в других частях кода.

package aplicacion.banco;

import java.util.ArrayList;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.EOFException;

public class OperadorDatos {

    private static final ArrayList<Persona> personas = new ArrayList<>();
    private static final ArrayList<CuentaBancaria> cuentasBancarias = new ArrayList<>();

    private static final File DIRECTORIO = new File ("data");
    private static final File PERSONAS = new File (DIRECTORIO.getPath() + "/" + "personas.dat");
    private static final File CUENTAS = new File (DIRECTORIO.getPath() + "/" + "cuentas.dat");

    /**
     * Comprueba la existencia de los archivos y directorio. Si no existen los crea.
     */
    private static void comprobarArchivos() {

        try {
            if (DIRECTORIO.exists()) {
                if (! PERSONAS.exists()) PERSONAS.createNewFile();
                if (! CUENTAS.exists()) CUENTAS.createNewFile();
            } else {
                DIRECTORIO.mkdir();
                PERSONAS.createNewFile();
                CUENTAS.createNewFile();
            }
        } catch (Exception e) {
            System.exit(-1);
        }
    }

    /**
     * Carga en memoria el contenido de los archivos.
     */
    public static void cargarArchivos() {

        comprobarArchivos();

        personas.clear();
        cuentasBancarias.clear();
        ObjectInputStream ois;
        try {
            ois = new ObjectInputStream(new FileInputStream(PERSONAS));
            while (true) {
                Persona p = (Persona)ois.readObject();
                personas.add(p);
            }
        } catch (EOFException eof) {
            // todo correcto
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            ois = new ObjectInputStream(new FileInputStream(CUENTAS));
            while (true) {
                CuentaBancaria cb = (CuentaBancaria)ois.readObject();
                cuentasBancarias.add(cb);
            }
        } catch (EOFException eof) {
            // todo correcto
        } catch (Exception e) {
            e.printStackTrace();
        }

        reasociar();
    }

    /**
     * Este metodo se encarga de reasociar cada Cuenta con su Persona al cargar el programa
     * en base al campo NIF que se entiende como clave unica (ya que no puede haber dos Persona con mismo nif)
     * Esto hace falta hacerlo ya que, como estoy serializando objetos que tienen como atributo otros objetos,
     * al volver a cargarlos la asociacion en direcciones de memoria se rompe. Este metodo se encarga de solucionar eso.
     */
    public static void reasociar() {

        for (CuentaBancaria cb : cuentasBancarias)
            for (Persona p : personas)
                if(cb.getPersona().getNif().equals(p.getNif()))
                    cb.setPersona(p);
    }

    /**
     * Comprueba si existe una persona con el nif indicado y devuelve un valor de posición
     * @param  nif nif a comprobar
     * @return     numero entero equivalente a la posicion de la persona en el array, -1 si no existe
     */
    public static int comprobarNif(String nif) {

        for (int i = 0; i < personas.size(); i++) {
            if (nif.equals(personas.get(i).getNif())) return i;
        }
        return -1;
    }

    /**
     * Crea un nuevo objeto Persona a partir de los datos,
     * actualiza el archivo de datos, y devuelve la Persona recien creada
     * @param  nif       NIF
     * @param  nombre    nombre
     * @param  apellidos apellidos
     * @return           el nuevo objeto Persona
     */
    public static Persona crearPersona(String nif, String nombre, String apellidos) {
        Persona p = new Persona(nif,nombre,apellidos);
        personas.add(p);
        actualizarArchivoPersonas();
        return p;
    }

    /**
     * Obtiene un objeto Persona a partir de busqueda por NIF
     * @param  nif NIF de la persona a buscar
     * @return     Objeto Persona al que corresponde este NIF, null si no existe.
     */
    public static Persona getPersona(String nif) {

        for (Persona p : personas) {
            if (p.getNif().equals(nif)) return p;
        }

        return null;
    }

    public static ArrayList<Persona> getListaPersonas() {
        return personas;
    }

    public static ArrayList<CuentaBancaria> getListaCuentas() {
        return cuentasBancarias;
    }

    /**
     * Crea un Objeto CuentaBancaria asociado a la Persona indicada,
     * actualiza el archivo, y devuelve el nuevo Objeto
     * @param  p Objeto Persona al que se asociará la nueva cuenta
     * @return   Objeto CuentaBancaria recién creado
     */
    public static CuentaBancaria crearCuentaBancaria(Persona p) {
        CuentaBancaria cb = new CuentaBancaria(cuentasBancarias.size()+1,p);
        cuentasBancarias.add(cb);
        actualizarArchivoCuentas();
        return cb;
    }

    /**
     * Crea un Objeto CuentaBancariaVip asociado a la Persona indicada,
     * actualiza el archivo, y devuelve el nuevo Objeto
     * @param  p Objeto Persona al que se asociará la nueva cuenta
     * @return   Objeto CuentaBancariaVip recién creado
     */
    public static CuentaBancariaVip crearCuentaBancariaVip(Persona p) {
        CuentaBancariaVip cb = new CuentaBancariaVip(cuentasBancarias.size()+1,p);
        cuentasBancarias.add(cb);
        actualizarArchivoCuentas();
        return cb;
    }

    /**
     * Actualiza el archivo personas.dat, que contiene informacion de las personas.
     */
    public static void actualizarArchivoPersonas() {

        try {

            PERSONAS.delete();
            PERSONAS.createNewFile();
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(PERSONAS, false));
            for (Persona p : personas) {
                oos.writeObject(p);
            }
            oos.close();

        } catch (Exception e) {

        }
    }

    /**
     * Actualiza el archivo cuentas.dat, que contiene informacion de las cuentas.
     */
    public static void actualizarArchivoCuentas() {

        try {

            CUENTAS.delete();
            CUENTAS.createNewFile();
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(CUENTAS, false));
            for (CuentaBancaria cb : cuentasBancarias) {
                oos.writeObject(cb);
            }
            oos.close();

        } catch (Exception e) {

        }
    }

    /**
     * Cuenta la cantidad de cuentas de una persona
     * y el valor total de las cuentas.
     * @param  p Persona cuyas cuentas contar
     * @return   array de dos posiciones, [0] es la cantidad de cuentas, [1] el valor total
     */
    public static double[] contarCuentas(Persona p) {

        double[] sum = new double[2];
        for (CuentaBancaria cb : cuentasBancarias) {
            if(cb.getPersona() == p) {
                sum[0]++;
                sum[1]+=cb.getSaldo();
            }
        }
        return sum;
    }
}

Проблема заключается в том, что при перезапуске программы и перезагрузке двух файлов в ArrayLists каждый BankAccount по-прежнему имеет Person с правильными данными, но этот Person теперь является объектом, отличным от Person, который хранится в персонализированном ArrayList, даже если они имеют одинаковые значения в своих полях.

Чтобы это исправить, у меня есть метод reasociar () также в этом классе, цель которого состоит в том, чтобы заново связать каждую учетную запись BankAccount с каждым лицом, соответственно, на основе этого nif лица (которое является уникальным идентификатором для каждого лица) сразу после загрузки данных из файлов. Если я не использую метод reasociar (), чтобы заставить Person в каждом BankAccount фактически указывать на правильного Person в personas ArrayList, объект Person в каждом BankAccounts - это разные элементы в памяти от Person, которые были прочитаны и сохранены для Персонал ArrayList, даже если с каждым BankAccount все еще связаны «правильный» nif, имя и фамилия владельца. Так что метод reasociar () отлично справляется с задачей, и благодаря этому все прекрасно работает при перезапуске программы.

Но мой вопрос ... Есть ли правильный способ сделать это, когда я пишу два файлов данных и считывания их при перезапуске программы, указатели объектов остаются согласованными без необходимости их повторной привязки? Надеюсь, я все хорошо объяснил ... Спасибо

...