Reflection - Instanz einer Klasse erstellen, ohne Default-Constructor

Kr0e

Gesperrter Benutzer
Hallo Zusammen!

ich frage mich, wie die Java Serialisierung es hinbekommt, Klassen beim Deserialisieren wieder zu erstellen, ggf. ohne dass diese Klasse einen public-default-constructor. Ich schreibe eine eigene kleine Serialisierungsvariante, aber hier hänge ich grad iwie.

Gruß,

Chris
 
Ich denke, Java erstellt kein Objekt und weist dem die Werte zu, sondern speichert beim Seriallisieren das komplette Objekt. Somit ist ein Aufrufen des (default)-Konstruktors nicht mehr notwendig.
 
Doch, Java erstellt neue Objekte und füllt alle Felder mit Reflection. Soviel weiß ich schon....

Aber die Frage ist halt, wie Java diese Constructoren beschafft... Aber habs nun gefunden und leider ist das nicht pure-Java wie ich gehofft hatte =(

Java:
    /** reflection factory for obtaining serialization constructors */
    private static final ReflectionFactory reflFactory = (ReflectionFactory)
	AccessController.doPrivileged(
	    new ReflectionFactory.GetReflectionFactoryAction());

Im ObjectOutputStream wird diese Factory genutzt, um einen Constructor von der JVM erstellen zu lassen, falls kein Default vorhanden ist... Darauf hat man natürlich als Programmierer keinen Zugriff.
com.sun.* Paket...

NAja, dann müssen meine Klassen halt zwingend einen Default-Konstruktor haben =(

Danke trotzdem!

Gruß,

Chris
 
Vllt hilfts:
Discover the secrets of the Java Serialization API
da gibt es einen Punkt: Create Your Own Protocol: the Externalizable Interface. Habe ich selber noch nie benutzt. Aber wenn du das Interface benutzt + die 2 Methoden zum schreiben+lesen definierst, könntest du (theoretisch) deine eigene Seriallsierung nutzen, wie die es vor hast.
 
Hallo!

Externalizable ist mir bereits bekannt, aber danke. Mich stört an Externalizable dass dort (auch wenn es immerhin ein Schritt in die richtige Richtung ist, das Speichern/Laden selbst zu managen), immernoch die ClassDesc gespeicher wird. Sprich der qualified Name der Klasse und noch die Länge des Namen als Short. Wenn man zig-tausend Objekte hat, stört mich der entstehende Overhead. Ich würde nur eine short-ID mitliefern. Auf beiden Seiten sind die short-ids nämlich bekannt.

Kryo ist natürlich auch eine Lösung, aber dort gehen rekursive Verlinkungen nciht. Deswegen brauch ich die Möglichkeit, das Objekt zu erstellen, bevor ich es fülle, damit ich das Object schonmal beim Lesen cachen kann und wiederauftretende Objekte aus dem Cache hole und nicht erneut schreibe...

Naja, vlt ist die Methode mit dem public Constructor garnicht verkehrt....

Danke nochmal!

Gruß,

Chris
 
Beim Suchen einer Lösung bin ich auf noch eine andere Variante egstoßen:

(Habt ihr davon schonmal gehört ?? - Ich nicht...)

JBoss Serialization - JBoss Community

Die bieten eine alternative Serialisierung an, die wohl schneller und performanter sein soll. Ebenfalls einfahc bedienbar wie die Java Serialisierung.

Gruß,

Chris


EDIT: Naja, das Problem bleibt das Gleiche, der basiert im Prinzip auf ObjectOutputStream mit ein paar Optimierungen....
 
Zuletzt bearbeitet:
Hätte auch noch eine (interessante) Lösung:
Java:
public class Tokenizer 
{	
	String data;
	
	private Tokenizer(String s, int i,JFrame f) 
	{
		data=s+i;
	}
	
	public String getData()
	{
		return data;
	}
	
	public static void main(String[] args) throws Exception 
	{		
		Constructor<?> c=Tokenizer.class.getDeclaredConstructors()[0];
		c.setAccessible(true);
		Vector<Object> v=new Vector<Object>();

		for(Class<?> classes:c.getParameterTypes())
			v.add(classes.isPrimitive() ? 0 : null);

		Tokenizer t=(Tokenizer) c.newInstance(v.toArray());
				
		System.out.println(t.getData());
	}
}

ein Konstruktor ist ja immer vorhanden und diese Methode setzt einfach immer null oder 0 ein.
 
Interessante Idee, hatte die gleiche idee, aber es scheitert leider daran, dass manche Constructoren Exceptions werfen können, wodurch viele Klassen nicht instanzierbar wären.

Ich glaube, ich versuchs mal mit Objenesis. Wobei dort beschrieben wird, dass es nicht mit jeder JVM klappen muss, wobei es dennoch bei den meisten funktioniert.

Gruß,

Chris

EDIT: Ich meine natülrich Exceptions, wenn gewisse Parameter null sind.
 
....
ein Konstruktor ist ja immer vorhanden und diese Methode setzt einfach immer null oder 0 ein.

Je nachdem was darin gemacht wird, fliegt ihm dann aber einen NPE um die Ohren.

Ich dachte eher an so etwas:
Java:
package vererbung;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import sun.reflect.ReflectionFactory;

public class Test {

	int i;

	public Test(String test) {
		System.out.println("CALLED with " + test);
	}

	public static void main(String[] args) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
		ReflectionFactory factory = ReflectionFactory.getReflectionFactory();
		Constructor<Test> constructor = factory.newConstructor(Test.class,
				new Class[0], new Class[0], Constructor.PUBLIC|Constructor.DECLARED, 0, "j  vererbung.Test.<init>()V+1", new byte[0],
				new byte[0]);
		constructor.setAccessible(true);
		Test t1 = constructor.newInstance();
		System.out.println(t1.i);
		//Set fields

	}
}

Aber das kracht aktuell auch noch...
 
Das hier gibt immer noch den einen/ursprünglichen Konstruktor aus:
Java:
		System.out.println(Arrays.toString(Test.class.getConstructors()));
		System.out.println(Arrays.toString(Test.class.getDeclaredConstructors()));
 
Kurze Frage: Wie kommst du an ReflectionFactory? Mein Eclipse findet nichts, natürlich hab ich die Improtanweisung drin 😉

Außerdem sollte man von der Benutzung von sun.* - Paketen doch absehen, oder ? Bzw. die könnten sich ja ändern in folgenden Releases.


Die ReflectionFactory hab ich auch gefunden, als ich den ObjectInputStream durchforstet hab...
 
Vlt ist doch die Externalizable am sinnvollsten (Wie Tomate_Salat bereits vorgeschlagen hat).
Anscheinend gibt es keine "offizielle" Lösung für dieses Vorhaben, die vom jetztigen Java-Sortiment angeboten wird. Objenesis muss dann wohl ähnlich "fragwürdige" Lösungen nehmen, wodurch auch der Warnhinweiß "Könnte in verscheidenen Java-Versionen nciht mehr funktionieren." klar wird.

Ohnehin stelle ich mir noch die Frage, ob es überhaupt Sinn macht, das selbst zu machen. Ich würde bei final-fields ja dann wieder Reflection nutzen, um die Felder quasi ansprechbar zu machen. Ic hweiß nicht, ob das wirklich eine Zeitersparnis wäre. Allerhöchstens vom Speicherbedarf, der ja bei Java-Standard-Serialisierung reht hoch ist.
 
So:
Java:
import java.lang.reflect.Constructor;

import sun.reflect.ReflectionFactory;

public class RFTest 
{
	private RFTest(String test)
	{
		test.charAt(0);
	}
	
	public void test()
	{
		System.out.println("HIER");
	}
	
	public static void main(String[] args) throws Exception
	{
		ReflectionFactory factory=ReflectionFactory.getReflectionFactory();
		Constructor<RFTest> constr = factory.newConstructorForSerialization(RFTest.class, Object.class.getDeclaredConstructor());
		RFTest test=constr.newInstance();
		test.test();
	}
}
funktioniert bei mir ohne zu krachen.
 

Zurück
Oben