# Reflection - Instanz einer Klasse erstellen, ohne Default-Constructor



## Kr0e (13. Jul 2011)

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


----------



## Tomate_Salat (13. Jul 2011)

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.


----------



## Kr0e (13. Jul 2011)

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 =(


```
/** 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


----------



## Tomate_Salat (13. Jul 2011)

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.


----------



## Kr0e (13. Jul 2011)

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


----------



## Gastredner (13. Jul 2011)

HotSpot greift, soweit ich weiß, auf die interne Klasse sun.misc.Unsafe zurück, um Instanzen ohne Konstruktoraufruf zu erstellen.


----------



## Kr0e (13. Jul 2011)

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....


----------



## ARadauer (13. Jul 2011)

objenesis - Objenesis - A library for instantiating Java objects - Google Project Hosting auch ganz interessant, wird zb von mockito verwendet


----------



## Tomate_Salat (13. Jul 2011)

Hätte auch noch eine (interessante) Lösung:

```
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.


----------



## Kr0e (13. Jul 2011)

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.


----------



## Ariol (13. Jul 2011)

Tomate_Salat hat gesagt.:


> ....
> 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:

```
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...


----------



## Ariol (13. Jul 2011)

Das hier gibt immer noch den einen/ursprünglichen Konstruktor aus:

```
System.out.println(Arrays.toString(Test.class.getConstructors()));
		System.out.println(Arrays.toString(Test.class.getDeclaredConstructors()));
```


----------



## Kr0e (13. Jul 2011)

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...


----------



## Tomate_Salat (13. Jul 2011)

Kr0e hat gesagt.:


> Kurze Frage: Wie kommst du an ReflectionFactory?.



ich bekomme die auch angezeigt in Eclipse.


----------



## Kr0e (13. Jul 2011)

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.


----------



## Tomate_Salat (13. Jul 2011)

So:

```
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.


----------

