Сериализация. Почему readObject не читает файл целиком?

Я новичок в Java и столкнулся с проблемой чтения/записи в текстовый файл.

Процесс :

  • читать и отображать текстовый файл: System.out.println(garage);
  • добавить первую машину: гараж.addVoiture(lag1);
  • добавить второй автомобиль:garage.addVoiture(A300B_2);

Вот main.java:

import fr.ocr.vehicule2.*;

public class Main {
    public static void main(String[] args) {
     Garage garage = new Garage();
     System.out.println(garage);

     Vehicule lag1 = new Lagouna();
     lag1.setMoteur(new MoteurEssence("150 Chevaux", 10256d));
     lag1.addOption(new GPS());
     lag1.addOption(new SiegeChauffant());
     lag1.addOption(new VitreElectrique());
     garage.addVoiture(lag1);

     Vehicule A300B_2 = new A300B();
     A300B_2.setMoteur(new MoteurElectrique("1500 W", 1234d));
     A300B_2.addOption(new Climatisation());
     A300B_2.addOption(new BarreDeToit());
     A300B_2.addOption(new SiegeChauffant());
     garage.addVoiture(A300B_2);
}

а вот и гараж.java

package fr.ocr.vehicule2;

import java.io.BufferedOutputStream;
import java.io.EOFException;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;


public class Garage {

  private List<Vehicule> voitures = new ArrayList<Vehicule>();;
  ObjectInputStream ois;
  ObjectOutputStream oos;

  public Garage(){
  }

  public String toString() {
    System.out.println("DEBUG start toString");

    String str = "";
    // Vérification de l'existence du fichier de sauvegarde
    if(Files.notExists(Paths.get("garage.txt"))) str += "Aucune voiture sauvegardée !\n";

    str += "*************************\n" 
       + "* Garage OpenClassrooms *\n"
       + "*************************\n";

    // Lecture du fichier texte
    if(Files.exists(Paths.get("garage.txt"))) {
      try {
        ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File("garage.txt"))));
          str += ((Vehicule)ois.readObject()).toString();
        ois.close();
      } catch (FileNotFoundException e) {
        e.printStackTrace();
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
      } catch (EOFException e) {
        e.printStackTrace();
      } catch (IOException e) {
        e.printStackTrace();
      } 
    }
    System.out.println("DEBUG end toString");
    return str;
  }

  public void addVoiture(Vehicule voit) {

    System.out.println("DEBUG start addVoiture" + voit);

    voitures.add(voit);

    // écriture du fichier texte
    try {
      oos = new ObjectOutputStream(
          new BufferedOutputStream(
            new FileOutputStream(
              new File("garage.txt"))));

//      for(Vehicule V:voitures){
//              oos.writeObject(V);
//          }
      oos.writeObject(voit);
      oos.close();

    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } 
    System.out.println("DEBUG end addVoiture");
  }
}

Первое выполнение main.java (текстовый файл не существует) я получаю в консоли:

  • Aucune voiture sauvegardée!

    • Garage OpenClassrooms *

  • начать addVoiture
  • конец addVoiture
  • начать addVoiture
  • конец addVoiture

Вот что я хочу показать в консоли при первом запуске.

Но при втором выполнении main.java (текстовый файл существует) я получаю в консоли:

  • DEBUG начать со строки
  • ОТЛАДКА end toString

    • Garage OpenClassrooms *

    • Voiture PIGEOT : Lagouna Moteur ELECTRIQUE 1500 W (1234.0) [Climatisation (347.3€), Barre de toit (29.9€), Siège chauffant (562.9€)] d'une valeur totale de 2174.1€
  • DEBUG start addVoiture+ Voiture RENO : Lagouna Moteur ESSENCE 150 Chevaux (10256.0) [GPS (113.5€), Siège chauffant (562.9€), Vitres electric (212.35€)] общей стоимостью 11144.75€
  • DEBUG конец addVoiture
  • DEBUG start addVoiture+ Voiture PIGEOT : Lagouna Moteur ELECTRIQUE 1500 W (1234,0) [Кондиционер (347,3€), Barre de toit (29,9€), Siège chauffant (562,9€)] общей стоимостью 2174,1€
  • DEBUG end addVoiture В консоли отображается только вторая машина (+ Voiture PIGEOT).

Результат, который я хочу, это оба из них, заказанные так же, как я их добавил:


  • _ * Garage OpenClassrooms * _ ****************************
    • Voiture RENO : Lagouna Moteur ESSENCE 150 Chevaux (10256.0) [GPS (113.5€), Siège chauffant (562.9€), Vitres electrique (212.35€)] d'une valeur totale de 11144.75€
    • Voiture PIGEOT : Lagouna Moteur ELECTRIQUE 1500 W (1234.0) [Climatisation (347.3€), Barre de toit (29.9€), Siège chauffant (562.9€)] d'une valeur totale de 2174.1€

Я не нахожу решения этой проблемы. Это потому, что я неправильно пишу обе машины: oos.writeObject(voit); или потому что я неправильно прочитал: str += ((Vehicule)ois.readObject()).toString(); ?

Я много искал даже в stackoverflow, но код кажется правильным. Так что может быть проблема в том, как я пишу, а затем читаю файл?

Большое спасибо, если кто-то может помочь, я трачу много времени, но оборачиваюсь.


Добавлено после информации @Kevin Anderson:


Еще раз большое спасибо за помощь!

Я изменил readObject с помощью вашего скрипта, но все равно получаю только последнюю добавленную машину.

Я также видел это сообщение (java.io.StreamCorruptedException: неверный код типа: AC) и не смог решить эту проблему. Поэтому я изменил способ записи Object (перестал добавлять объекты в ObjectOutputStream) и получил почти хороший результат.

Теперь моя последняя проблема связана с «Безопасностью типов: непроверенное приведение от объекта к списку» в readObject().

Я думаю (не уверен?), что это просто информация IDE, поэтому я полагаю, что было бы лучше сделать это правильно, поэтому я хотел бы избежать использования (@SuppressWarnings("unchecked")) над (public String toString() { ) и сделать это наилучшим образом, но мне не удалось применить ни одно из всех решений, которые я нашел в Stackoverflow.

Если у вас есть идея, это было бы действительно прекрасно!

Вот новый гараж.java:

public class Garage {

  private List<Vehicule> voitures = new ArrayList<Vehicule>();
  private ObjectInputStream ois;
  protected static ObjectOutputStream oos;

  public Garage(){}

  public String toString() {

    String str = "";

    // Vérification de l'existence du fichier de sauvegarde
    if(Files.notExists(Paths.get("garage.txt"))) {
      str += "Aucune voiture sauvegardée !\n";
    }

    str += "*************************\n" 
       + "* Garage OpenClassrooms *\n"
       + "*************************\n";

    // Lecture du fichier texte
    if(Files.exists(Paths.get("garage.txt"))) {
      try {
        ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File("garage.txt"))));

        // garage loading during instanciation :
              // Information Eclipse : 
              // Type safety: Unchecked cast from Object to List<Vehicule>
              this.voitures = (List<Vehicule>)ois.readObject();

        // Affichage des voitures enregistrées dans le fichier texte
        for(Vehicule V : this.voitures){
          str += V;
        }


              // To avoid "Type safety: Unchecked cast from Object to List<Vehicule>" :
        // those 2 solutions show identical information "Type safety: Unchecked cast from Object to List<Vehicule>"

        // First tried solution :
        // this.voitures = new new ArrayList<Vehicule>((List<Vehicule>)ois.readObject()); 

              // Second tried solution :
//              if(ois.readObject() instanceof List<?>){
//                  List<?> list = (ArrayList<?>)ois.readObject();
//                  for(Object e : list){
//                     if(e instanceof Vehicule){
//                       this.voitures = (List<Vehicule>)e;
//                     }
//                  }
//              }


        ois.close();
      } catch (FileNotFoundException e) {
        e.printStackTrace();
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
      } catch (EOFException e) {
        e.printStackTrace();
      } catch (IOException e) {
        e.printStackTrace();
      } 
    }
    return str;
  }

  public void addVoiture(Vehicule voit) {

    voitures.add(voit);

    // écriture du fichier texte
    try {
      oos = new ObjectOutputStream(
          new BufferedOutputStream(
            new FileOutputStream(
              new File("garage.txt"))));

      // Enregistrement detoutes les voitures dans le fichier texte
      oos.writeObject(voitures);

      oos.close();
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    } 
  }
}

Добавлено решение для безопасности типов: непроверенное приведение от объекта к списку.


Благодаря этому сообщению: Безопасность типов: Unchecked cast

Текстовый файл содержит объект списка, и когда я запускаю Garage(), мне нужно было добавить этот объект списка к существующим спискам.

Eclipse показывал: Безопасность типа: непроверенное приведение от объекта к списку

Я мог бы использовать : @SuppressWarnings("unchecked") для своего метода, но лучше сделать так, поэтому я не игнорирую никакие предупреждения:

    List<?> list = (List<?>) ois.readObject();
    for(int i = 0; i < list.size(); i++) {
      this.voitures.add((Vehicule) list.get(i)); // instanciation
      str += (Vehicule) list.get(i);// Added to console message
    }       

Надеюсь, это поможет, извините за мои ошибки новичка ^^, и спасибо всем за помощь!


person kobano    schedule 23.11.2017    source источник
comment
Вы продолжаете писать the text file exists, но ObjectOutputStream пишет двоичный код, а не текст.   -  person Kayaman    schedule 23.11.2017


Ответы (2)


Каждый раз, когда вы вызываете addVoiture, вы перезаписываете файл данных, а не дополняете его. Измените код открытия файла на

oos = new ObjectOutputStream(
      new BufferedOutputStream(
        new FileOutputStream(
          new File("garage.txt"), true)));

Этот дополнительный аргумент true на new FileOutputStream открывает существующий файл для добавления вместо того, чтобы каждый раз создавать новый пустой файл. См. документы здесь

Кроме того, поскольку вы записываете несколько объектов Vehicule в свой файл, вам нужно выполнить несколько вызовов readObject, чтобы снова прочитать их все. Причина, по которой вы видите только одну машину, заключается в том, что вы звоните readObject только один раз. Вам нужно продолжать вызывать readObject в цикле, пока не получите EOFException:

try {
        ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream(new File("garage.txt"))));
        for(;;){
            try {
               str += ((Vehicule)ois.readObject()).toString();
            } catch (EOFException eofe) {
               break;
            }
        }
        ois.close();
      }
} catch ....

С этим изменением вам больше не нужно перехватывать EOFException с помощью внешнего блока try, потому что теперь его обрабатывает внутренний try.

person Kevin Anderson    schedule 23.11.2017
comment
Спасибо всем за перезапись информации. Но я добавил параметр true в FileOutputStream для добавления, и (str += ((Vehicule)ois.readObject()).toString();) по-прежнему возвращает только последний добавленный автомобиль (Voiture PIGEOT: Lagouna Moteur ELECTRIQUE). Я предполагаю, что это означает, что файл содержит только последний добавленный автомобиль. Есть конечно еще одна проблема :/ ? - person kobano; 23.11.2017
comment
Еще раз спасибо, я обновил код, но, наконец, изменил способ записи объекта и получения другой информации. см. обновленный пост. - person kobano; 25.11.2017
comment
Все в порядке, я нашел способ избежать предупреждения во время приведения списка. - person kobano; 01.12.2017

Название говорит вам, что вам нужно сделать: прочитать объект.

Он не называется read File.

Другими словами: когда вы используете потоки объектов, ваш код определяет, какие объекты записываются в каком порядке. А затем, с другой стороны, аналогичный код должен читать эти объекты в том же порядке.

Это все, что нужно для этого.

И ваш текущий код перезаписывает существующий файл. Если вы хотите «сохранить» содержимое файла, вам сначала нужно прочитать объекты, которые были сохранены ранее.

person GhostCat    schedule 23.11.2017