Objektserialisierung, Transient

Status
Nicht offen für weitere Antworten.

SnooP

Top Contributor
Moin... - ich bin immer noch bei meinem Problem Objekte "tief" zu kopieren, auch wenn mein ReflectionCopier inzwischen fertig ist, ist mir beim Implementieren aufgefallen, dass viele Klassen der Java-API Felder benutzen, die transient sind. Diese Felder sind dann aber auch noch nichtmal Attribute, die für den Objektzustand irrelevant sind sondern ganz im Gegenteil sehr relevant, wie etwa das Datenarray in einer ArrayList (private transient E[] elementData).

Meinem ReflectionCopier ist ein transient Feld momentan noch relativ egal, so dass ich das Feld einfach mitkopieren kann und im Ergebnis auch keine Probleme bekomme. Der ObjectOutputStream hingegen kann ja nicht auf dieses Feld direkt zugreifen und bemüht daher die write bzw. readObject-Methode der Klasse, die genau dieses Kopieren über nen gewissen Umweg ermöglicht.

Die Frage ist jetzt - warum macht man das so? Was ist der Vorteil, bzw. warum ist dieses Feld transient?

Ich hatte nämlich auch überlegt das transient Schlüsselwort zu nutzen, um dem Nutzer meines Copiers eine Möglichkeit zu geben, Felder explizit vor dem Kopieren zu schützen. Allerdings funktioniert das ganze dann natürlich überhaupt nicht mehr, da ich auf den Inhalt dieser Felder natürlich angewiesen bin ;)
 

Wildcard

Top Contributor
transient wird verwendet wenn es sich um Laufzeit Daten handelt die für die Speicherung irrelevant sind, da sie bspw. beim Programmstart neu gesetzt werden. Für ein DeepCopy solltest du diese Felder also kopieren, für eine Persistierung jedoch nicht.
 

SnooP

Top Contributor
versteh ich aber nicht, da wie gesagt ArrayList die Daten in einem transient Array hält... - bei einer Persistierung würden die flöten gehen, wenn nicht die writeObject Methode von ArrayList da wäre.

Von daher würd ich halt gerne wissen, warum das so gelöst wurde... imho passiert bei der writeObject Methode auch nix schlimmeres mehr, als bei einer "normalen" Serialisierung passieren würde.

P.S. @wildcard: mein bisheriger ReflectionCopier ist darüberhinaus auch noch ein wenig buggy, wenn mehr als eine Kopie von einem Objekt erzeugt werden soll, wird nämlich momentan immer dieselbe genommen ;) - da hab ich einfach vergessen die Hashmap beim Start zu resetten. Darüberhinaus bekommt man mit der Hashmap auch unter Umständen probleme, wenn die Objekte hashcode überschreiben, da ich ja die Objekte als key nutze, kann es da zu falschen Einsortierungen kommen. Ist inzwischen behoben, werde ich demnächst neu hochladen und dann nochmal bescheid geben.
 
B

bygones

Gast
SnooP hat gesagt.:
versteh ich aber nicht, da wie gesagt ArrayList die Daten in einem transient Array hält... - bei einer Persistierung würden die flöten gehen, wenn nicht die writeObject Methode von ArrayList da wäre.
ist sie ja aber, daher versteh ich nicht das Problem ?!
 

Wildcard

Top Contributor
SnooP hat gesagt.:
versteh ich aber nicht, da wie gesagt ArrayList die Daten in einem transient Array hält... - bei einer Persistierung würden die flöten gehen, wenn nicht die writeObject Methode von ArrayList da wäre.

Von daher würd ich halt gerne wissen, warum das so gelöst wurde... imho passiert bei der writeObject Methode auch nix schlimmeres mehr, als bei einer "normalen" Serialisierung passieren würde.
Ich weiß nicht auf welche Klassen du dich konkret beziehst, aber hier mal ein (eher dämliches Beispiel):
Angenommen ein Objekt speichert aus Gründen der Speicherverschwendung alle Mausklicks des Benutzers während das Programm läuft. Diese Daten sind allerdings völlig sinnlos und sollen auch nicht persistiert werden da sie jedesmal neu erzeugt werden. Einfachste Lösung: transient machen

Ich verstehe dein Problem nicht so wirklich. Der ObjectOutputStream sollte diese ArrayList eigentlich komplett ignorieren ???:L
 

SnooP

Top Contributor
also nochmal ;) - ich poste mal den releveanten Code der java.util.ArrayList:

Code:
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer.
     */
    private transient E[] elementData;

//[...]
 /**
     * Save the state of the <tt>ArrayList</tt> instance to a stream (that
     * is, serialize it).
     *
     * @serialData The length of the array backing the <tt>ArrayList</tt>
     *             instance is emitted (int), followed by all of its elements
     *             (each an <tt>Object</tt>) in the proper order.
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
	int expectedModCount = modCount;
	// Write out element count, and any hidden stuff
	s.defaultWriteObject();

        // Write out array length
        s.writeInt(elementData.length);

	// Write out all elements in the proper order.
	for (int i=0; i<size; i++)
            s.writeObject(elementData[i]);

 	if (modCount != expectedModCount) {
	    throw new ConcurrentModificationException();
	}
    }
//[...]

Die relevanten Daten werden also demnach in dem transient Feld "elementData" gespeichert und sind somit vor einer Serialisierung geschützt. Was natürlich verblüfft, weil die Daten bei einer Persistenzierung ;) - verloren gehen würden. Aaaber - es gibt dafür innerhalb von ObjectOutputStream eine Callback-Methode die von den zu speichernden Klassen geschrieben werden können, namens writeObject und readObject. Diese werden offenbar immer dann genutzt, wenn die Serialisierung nicht mit der "normalen" Methode funktionieren würde. Bei der ArrayList halt genau deswegen nicht, weil das Datenfeld halt transient ist.

Und daher die Frage, warum ist das Feld überhaupt transient ;)

Perverserweise gilt dies offenbar für nahezu alle Collections in java. Beispielsweise ist das Feld "header" und "size" der linked List ebenfalls transient und somit mithilfe der normalen Serialisierungsmethode halt nicht zu serialisieren... auch dort wird der umweg über writeObject gegangen... - die Frage ist natürlich wieso ;) .. und müsste ich mir auch ähnliche Gedanken machen, d.h. macht das in bestimmten Fällen tatsächlich Sinn, sodass es bei meiner Implementierung krachen könnte? ;)
 

Wildcard

Top Contributor
Ein Array ist auch ein Objekt. Es gibt keinen Grund das Array zu speichern, da nur dessen Inhalt relevant ist.
Gibt vieleicht auch noch andere Gründe...
 

byte

Top Contributor
Man kann doch eine ArrayList nur dann serialisieren, wenn alle Elemente des Arrays Serializable implementieren. Davon kann ja aber nicht per se ausgegangen werden. Evtl. ist das der Grund, warum stattdessen diese writeObject(), readObject() Methoden verwendet werden!?

Nur so ne Idee... :roll:


Wildcard hat gesagt.:
Ein Array ist auch ein Objekt. Es gibt keinen Grund das Array zu speichern, da nur dessen Inhalt relevant ist.

Hä? Die Begründung verstehe ich jetzt nicht. Warum implementiert ArrayList dann Serializable, wenn die Rohdaten (also das zugrunde liegende Array) Deiner Meinung nach nicht gespeichert werden braucht? Was passiert dann mit den Elementen? Schweben sie dann im luftleeren Raum? :bae:
 

Wildcard

Top Contributor
byto hat gesagt.:
Hä? Die Begründung verstehe ich jetzt nicht. Warum implementiert ArrayList dann Serializable, wenn die Rohdaten (also das zugrunde liegende Array) Deiner Meinung nach nicht gespeichert werden braucht? Was passiert dann mit den Elementen? Schweben sie dann im luftleeren Raum? :bae:
Die Daten werden gespeichert. Nur eben das Array nicht. Siehe Source...
 

SnooP

Top Contributor
Die Objektserialisierung würde dann aber beim Speichern der Elemente des Arrays abbrechen, wenn eins nicht serializable wäre und die Exception werfen... genau das gleiche würde auch passieren, wenn die writeObject Methode auf ein solches Element stößt...

Die Daten des Arrays würden mit der "normalen" Serialisierungsmethode genauso gespeichert werden, wenn das Feld nicht transient wäre... - ich kann ja da nochmal weiterforschen ;) - bislang hab ich jedenfalls keine befriedigende Antwort gefunden.
 

SnooP

Top Contributor
So... der direkte Vergleich der beiden relevanten Methoden macht es nochmal klarer... hier die Methode die im ObjectOutputStream aufgerufen wird, wenn ein Array gespeichert werden soll:
Code:
//[...]
    private void writeArray(Object array, 
			    ObjectStreamClass desc, 
			    boolean unshared) 
	throws IOException 
    {
    //[...]//für alle möglichen Array-Typen Einzelbehandlungen

   //für den Fall eines Object[] Arrays:
	} else {
	    Object[] objs = (Object[]) array;
	    int len = objs.length;
	    bout.writeInt(len);
	    for (int i = 0; i < len; i++) {
		writeObject0(objs[i], false);
	    }
	}
//...
Verglichen mit der writeObject-Methode gibt es keinen wirklichen unterschied... - ich bin momentan sogar versucht die ArrayList umzuschreiben ohne transient Feld und beides mal durch meine JUnit laufen zu lassen... ich möchte wetten, dass es keine Unterschiede geben wird ;)

Edit: okay - korrigiere, die ConcurrentModificationException kann in der writeObject-Methode geworfen werden, wenn beim Serialisieren an der Liste manipuliert wird. Das könnte die Erklärung sein... - ist sie's auch? ;) - Meinungen?
 

Wildcard

Top Contributor
Ja, das Ergebnis würde wohl das Gleiche sein. Kann's eigentlich nur noch um die ConcurrentModificationException gehen...
 

SnooP

Top Contributor
Wenn es nur darum geht, dann kann ich damit glaub ich ganz gut leben. Die Objekte die ich kopieren will, sollten nicht aktiv sein, während des Kopierens... wär ja auch noch schöner ;)
Ansonsten kann ich das ja noch als Bedingung reinstellen... - ich kann mich ja nicht um alles kümmern ;)

By the way ist der ReflectionCopier durch die Veränderungen doch inzwischen deutlich langsamer geworden... ich schätze so gute 5 mal langsamer als die Objektserialisierung... evtl. könnte ich einsparen, wenn ich nicht auto-boxing verwenden würde (war ich zu faul zu).. trotzdem hat man mit meinem Ansatz die Möglichkeit alle Klassen zu kopieren... das ist insb. dann sinnig, wenn man zich klassen vorgegben hat oder dynamisch laden soll, die nicht von sich aus serializable implementieren. Man hat ja evtl. doch andere Dinge zu tun, als erst alles zu refactorn ;)
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben