ObjectInput/OutputStream Fragen zur Funktionsweise

TpointO

Neues Mitglied
Hallo zusammen,

für eine Übung soll ich ein Adressbuch schreiben. Dabei soll ich eine Klasse Adresse benutzen welches Serializable implementiert. Außerdem soll die Klasse die Methoden "public static void schreiben (Adresse [] adr)" und "public static void lesen (Adresse [] adr)" besitzen. Für diese Methoden benutze ich den ObjectInput - bzw. ObjectOutputStream.
Ich hab das jetzt so gehandhabt, dass in der Klasse Adressbuch die Adressen erzeugt werden und dann die Methode schreiben und danach lesen aufgerufen wird. Das Funktioniert soweit auch.
Das Problem ist aber, dass es für ein Adressbuch natürlich sinnvoll wäre wenn man Adressen hinzufügen könnte und das Funktioniert bei mir nicht.
Jedes mal wenn ich das Programm starte und über einen Scanner Adressen hinzufüge wird die "adressen.ser" Datei zwar länger und wenn man genau hinschaut sieht man auch die neuen Straßennamen, im Programm selber werden aber immer nur die Adressen angezeigt die man beim ersten Programmstart eingefügt hat (also beim allerersten Start, dann wenn die .ser Datei angelegt wird). Das ganze habe ich dann zusätzlich noch mit einer ArrayList probiert aber hier verhält sich das Programm gleich. Soweit zu meinem Problem.

Nun meine Frage:
Ich schätze ich habe ein Verständnisproblem wie genau ObjectInputStream und ObjectOutputStream funktionieren. Ich meine, dass der ObjectOutputStream das Object (also das AdressenArray) liest und in Bytes umwandelt. Diese Bytes schreibt er dann in die .ser Datei. Der ObjectInputStream liest dann diese .ser Datei und wandelt die Bytes wieder in ein Object um. Dieses Object kann dann wieder ganz normal behandelt werden. Meine Vermutung ist, dass mit jedem Programmstart ein neues Object in die .ser Datei geschrieben wird aber er immer nur ein Object liest und zwar das erste. Ist das soweit Richtig? Wenn ja, müsste ich dann die Objecte miteinander verknüpfen, sodass diese wieder nur ein einziges Object sind? Geht das? Und wenn ja wie? Wenn nein, wie sollte ich nun weiter vorgehen? Ist es möglich die Objecte nacheinander auszulesen?
Ich hab schon viel gegooglet aber bis jetzt noch keine Antwort auf meine Fragen gefunden. Wäre nett wenn mir jemand weiterhelfen könnte.

Ganz schön viel Text, tut mir Leid aber ich hoffe man versteht worin mein Problem liegt.
 
Du müsstest uns einmal genau den Code zeigen, den Du verwendest. Wie schreibst / liest du die Adressen genau?

Vermutlich hängst Du einfach die neue Adresse hinten an die Datei an, also du öffnest einen ObjectOutputStream auf einem FileOutputStream mit append=true?

Das Problem dabei wäre, dass die Erstellung des ObjectOutputStreams direkt einen Header schreibt:
https://docs.oracle.com/javase/7/do...html#ObjectOutputStream(java.io.OutputStream):
"This constructor writes the serialization stream header to the underlying stream;"

Was Du machen könntest:
a) Entweder immer das ganze Array schreiben, d.h. den Inhalt immer komplett überschreiben.

b) Deutlich komplexer: Du schreibst Deine eigene Serialisierung. Könnte auch auf ObjectStream basieren - brauchst dann "nur" eine Variante, die den Header beim Anhängen nicht schreibt.

Edit: BTW: Was mich irritiert ist, dass du keinen Fehler angibst. Evtl. hast Du try/catch Konstrukte mit leerem catch oder so? Wenn das beschriebene Szenario Dein Szenario ist, dann solltest Du eine StreamCorruptedException bekommen haben. Aber denkbar ist natürlich auch, dass Du das Array als Array geschrieben hast und du nur dieses liest, d.h. Du schreibst ein Array mit n Elementen, dann hängst du einzelne Elemente an ... beim Lesen wird aber nur das Array mit den n Elementen gelesen ....
 
Vermutlich hängst Du einfach die neue Adresse hinten an die Datei an, also du öffnest einen ObjectOutputStream auf einem FileOutputStream mit append=true?
Richtig.

Fehler werden mir keine Angezeigt.

Das Problem dabei wäre, dass die Erstellung des ObjectOutputStreams direkt einen Header schreibt:
https://docs.oracle.com/javase/7/do...html#ObjectOutputStream(java.io.OutputStream):
"This constructor writes the serialization stream header to the underlying stream;"
Danke, dann weiß ich schonmal wo ich suchen muss 😀

Anbei mein Code

Code:
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.Scanner;
import java.io.Serializable;
import java.io.FileInputStream;
import java.io.ObjectInputStream;


class Adresse implements Serializable
{
    int plz;  
    String str;
    int hausnmr;
   
   
    public Adresse (int plz, String str, int hausnmr)
    {
        this.plz = plz;
        this.str = str;
        this.hausnmr = hausnmr;
    }
    public void setPlz(int plz)
    {
        this.plz = plz;
    }
    public void setHausnmr(int hausnmr)
    {
        this.hausnmr = hausnmr;
    }
    public void setStr(String str)
    {
        this.str = str;
    }
    public String toString()
    {
        return plz +" "+ str + " " + hausnmr;
    }
    public static void schreiben (Adresse [] adr)
    {
        try
        {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("adressen.ser", true));
            oos.writeObject(adr);
            oos.flush();
            oos.close();
        }
        catch(Exception e){System.out.println(e);}
    }
   
    public static void lesen (Adresse [] adr)
    {
        try
        {
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("adressen.ser"));
            Adresse[] ad = (Adresse []) in.readObject();                                      
           
           
            for (int i = 0; i<ad.length; i++)
            {      
                if(ad[i] != null)
                System.out.println(ad[i]);
           
            }
           
        }

        catch(Exception e){System.out.println(e);}
    }
}

public class Adressbuch2
{
    public static void main (String [] args)
    {
        Scanner scanner = new Scanner(System.in);
       
        Adresse [] a = new Adresse [20];
       
        for (int i = 0; i<a.length; i++)
        {
            a [i] = new Adresse(0, null, 0);
            System.out.println("PLZ: ");
            int plz = Integer.parseInt(scanner.nextLine());
            a[i].plz = plz;
            System.out.println("Strasse: ");
            String str = scanner.nextLine();
            a[i].str = str;
            System.out.println("Hausnummer: ");
            int hausnmr = Integer.parseInt(scanner.nextLine());
            a[i].hausnmr = hausnmr;
            System.out.println("Weiter?: ");
            char yesNo = scanner.nextLine().charAt(0);
            if (yesNo == 'n')
                break;
        }
            a[0].schreiben(a);  
            a[0].lesen(a);  
                                           
    }
}

Edit: Was ich auch nicht verstehe ist, dass man keine for-Schleife benötigt zum einlesen. Ein "a[0].schreiben(a);" reicht aus. Dabei spielt es keine Rolle ob a[0] oder a[1] oder a[irgendwas]. Warum ist das so?
 
Zuletzt bearbeitet:
Die Methode schreiben ist eine statische Methode, d.h. sie existiert in der Klasse.
Daher solltest Du diese auch nicht auf einer Instanz sondern auf der Klasse selbst aufrufen. Statt also a[0].schreiben(a); solltest Du Adresse.schreiben(a) schreiben.

Und dann solltest Du bei ObjectOutputStream nicht anhängen sondern immer überschreiben (Auf Grund des headers, der geschrieben wird).

Und lesen sollte doch kein array übergeben bekommen sondern bestimmt ein array zurück geben, oder?
Also public static Adresse[] lesen ()

Die Adressen solltest Du immer in einer Collection speichern, die von der Größe her dynamisch ist. List würde ich da anbieten mit der konkreten Implementierung ArrayList.

Und du kannst dann z.B. schreiben:
Code:
List<Adresse> adressen = Arrays.toList(Adresse.lesen());
// adressen.add(new Adresse(.....));
Adresse.speichern(adressen.toArray(new Adresse[adressen.size()]);

Du liest also die Adressen mit Adresse.lesen() und mittels Array.toList wandelst Du es in eine List<Adresse> um.
Diese Liste kannst Du dann verändern.
Zum Speichern brauchst Du wieder ein Array. Dazu gibt es dann die Methode toArray. Da wir aber statt einem Array von Object ein Array von Adresse haben wollen, müssen wir die Version nehmen, die so ein Array übergeben bekommen will....

Der Code zeigt dann das hinzufügen einer Adresse. Du liest alle Adressen ein, fügst die neue adresse hinzu und speicherst dann alle Adressen ab.
 

Zurück
Oben