Martin Jung: Objekte, Verweise

Beitrag lesen

Hi,

Ich habe also innerhalb von selbstefinierten Klassen, Verweise auf weitere selbstefinierte Klassen. Wenn ich nun eine Instanz der Klasse t1 über ein Socket versende (die Klassen sind alle serialisierbar),

Nein, nach dem Standardverfahren zumindest nicht (ohne weiteres) mit Deinem Beispiel-Code.
Aus folgendem Beispiel (etwas länger, sollte lauffähig sein):

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Calendar;
import java.util.Vector;

public class TestStandardSerialization {

static final String TARGET_DIR = "D:\temp";
    static final String SERIAL_FILE_NAME = "TestSerialisation.ser";
    static final File   SERIAL_FILE = new File(new File(TARGET_DIR), SERIAL_FILE_NAME);

public static void main(String[] args) {
        FileOutputStream out;
        FileInputStream in;
        ObjectOutputStream os;
        ObjectInputStream is;

// object muss java.io.Serializable implementieren
        try {
            out = new FileOutputStream(SERIAL_FILE);
            os = new ObjectOutputStream(out);
            os.writeObject(new NonSerializable());
            os.close();
        } catch (IOException e) {
            System.err.println(e.toString());
        }

// alle nicht-statischen[*] und nicht-transienten muessen java.io.Serializable implementieren
        try {
            out = new FileOutputStream(SERIAL_FILE);
            os = new ObjectOutputStream(out);
            // transiente werden ignoriert
            os.writeObject(new SerializableWithTransient());
            // beachte: die Exception wird _erst_ durch folgendes Statement erzwungen
            os.writeObject(new SerializableNoTransient());
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // [*] statische Member gehoeren nicht zu einer Instanz dieser Klasse

// recursive successful write
        try {
            out = new FileOutputStream(SERIAL_FILE);
            os = new ObjectOutputStream(out);
            System.out.println("write start: " + timeStamp());
            os.writeObject(new SerializableRecursive());
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("example failed!");
        }

// wait some time
        try {
            Thread.sleep(5 * 1000L);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
            throw new RuntimeException("example failed!");
        }

// read again
        try {
            in = new FileInputStream(SERIAL_FILE);
            is = new ObjectInputStream(in);
            System.out.println("read start: " + timeStamp());
            SerializableRecursive toSerialize = new SerializableRecursive();
            toSerialize = (SerializableRecursive) is.readObject();
            is.close();
            // print container
            System.out.println(toSerialize.cont.vec);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("example failed!");
        }
    }

static String timeStamp() {
        return Calendar.getInstance().getTime().toString();
    }

// --------------------------------------------------- inner classes
    static class NonSerializable {
    }

static class SerializableWithTransient implements Serializable {
        public transient NonSerializable sMember = new NonSerializable();
    }

static class SerializableNoTransient implements Serializable {
        public NonSerializable nsMember = new NonSerializable();
    }

static class SerializableRecursive implements Serializable {
        public AnyContainer cont = new AnyContainer();
    }

static class AnyContainer implements Serializable {
        public Vector vec = new Vector();
        public AnyContainer() {
            this.vec.addElement("Object written: " +TestStandardSerialization.timeStamp());
        }
    }
}

geht hervor:

  • dass die finale(!) Methode writeObject(Object) nur Instanzen von Klassen, die das Marker-Interface <java.io.Serializable> implementieren, serialisiert[1]. Implizit von der Serialisierung ausgeschlossen sind statische Member (gehören zur Klasse). Explizit alle diejenigen, die mit dem modifier <transient> gekennzeichnet sind.

  • writeObject(Object) funktioniert rekursiv. Dabei wird versucht, das durch alle nicht-statischen und nicht-transienten Member gebildete Objekt-Geflecht in einen (Byte)Strom zu überführen (dabei Abbruch, wenn vorherige Bedingung nicht erüllt ist).
    Zu beachten:

  • Das rekursive Verhalten kann leicht dazu führen, dass mehr Objekte als erwartet serialisiert werden.
  • Und dass während dieses möglicherweise unerwartet lange dauernden Vorgangs die GarbageCollection dieser Objekte noch blockiert ist, weil noch intern Referenzen darauf gehalten werden (auch wenn im restlichen Porgrammfluss keinerlei Referenzen darauf mehr bestehen sollten).

[1]
Das finde ich unglücklich, da es aus der Signatur der Methode alleine nicht hervorgeht (und man ein Implementierungsdetail zusaetzlich kennen muss). Da in Java alle Klassen implizit von Object erben, hätte ich hier einen Parameter vom Typ Serializable erwartet.

[entfernt..] werden dann automatisch alle Verweise auf t2 und t3 mitgesandt? Kann ich dann in der Klasse, die die Daten empfängt darauf gehen, dass ich auf das Objekt t3 zugreifen kann und dort das Element Test in diesem Vektor vorhanden ist? Wenn nicht, wie kann ich das sicherstellen?

siehe oben.

Viele Grüße,
Martin Jung