# Klasse aus String



## Sunchezz (4. Mai 2011)

Halli hallo liebe java Anhänger,

irgendwie bin ich heute nacht beim einschlafen auf diese Frage gestoßen, mal schauen ob es jemand weiß...
hab auch schon was gefunden, aber wohl nich das was ich wirklich brauch.

Also, angenommen ich habe in einem Ordner eine .class datei!

Nun starte ich folgendes Programm... (ok, leicht vereinfacht)

```
public static void main(String[] args) {
  FileReader r = new FileReader(classFile)
  blabla...
  String s = r.read(); // kompletter Dateiinhalt
  Class  newClass = Class.parse(s); // So, diese Zeile is nun... naja, etwas meiner Fantasie entsprungen
  Object obj = newClass.newInstance();
}
```


Also, gibt es irgendeine möglichkeit zur laufzeit aus einem String eine Klasse zu machen?
Ich weiß ja das ich mit UrlClassloader und nem normalen classloader mir mit forName("Classname") die klasse auch so laden kann...
aber was nun wenn ich die klasse verschicken will... zum beispiel übers internet, kann ich dann irgendwie zur laufzeit daraus ne class machn, ohne sie in nem (temporären) file zu speichern?
also das der classinhalt nur in der JVM bleibt?

Hoffe meine Frage is klar geworden =)


----------



## Gast2 (4. Mai 2011)

Das solltest du mit der JavaCompiler API hinkriegen, siehe z.b.:
Java Compiler API, Teil 1: Grundlagen


----------



## splinter (4. Mai 2011)

EikeB hat gesagt.:


> Das solltest du mit der JavaCompiler API hinkriegen, siehe z.b.:
> Java Compiler API, Teil 1: Grundlagen



Wenn die Klasse aber in Byte-Code vorliegt reicht auch ein Classloader:


```
File classFile = new File("BlaBla.class");
InputStream in = new FileInputStream(classFile);

byte[] bytecode = new byte[classFile.length()];
in.read(bytecode); // oder entsprechend übers Netzwerk laden.

ClassLoader classloader = ClassLoader.getSystemClassLoader(); // oder anderen Classloader verwenden.
Class clazz = classloader.defineClass("BlaBla", bytecode, 0, bytecode.length);

Object obj = class.newInstance();
```


----------



## Gast2 (4. Mai 2011)

Jo, dann reicht das. 


> Also, gibt es irgendeine möglichkeit zur laufzeit aus einem String eine Klasse zu machen?


Ich habe mich aber irgendwie hierdrauf bezogen.


----------



## splinter (4. Mai 2011)

Sunchezz hat gesagt.:


> Also, angenommen ich habe in einem Ordner eine .class datei!



Und ich mich darauf!


----------



## Gast2 (4. Mai 2011)

Dann darf sich der TO jetzt mal klar werden was er genau will


----------



## Spacerat (4. Mai 2011)

Irgendwo hatte ich mal diverse Classloader gefunden, die entweder mit dem Eclipse JDT Compiler oder mit dem Sun Compiler (com.sun.tools.javac.Main) arbeiten. Diese versuchten zunächst die Klasse zu laden. Gelang das nicht (Exception) wurde versucht die Klasse zu kompilieren und erneut versucht diese zu laden. Erst wenn alles fehl schlug wurde die ClassNotFoundException geworfen. Es ist gut möglich, dass dieses in seiner Gesamtheit ein riesiges Sicherheitsloch darstellte und ich den Code deswegen nicht mehr finde. Zumindest fand ich noch ein Beispiel, wie man den Sun Kompiler verwendet.
	
	
	
	





```
import com.sun.tools.javac.Main;
 
public class ToolsTest {
    public static void main(String[] args) {
        //Parameter analog zum normalen Aufruf von javac über die Konsole...
        Main.compile(new String[]{"C:/eclipse/workspace/testproject/src/ABC.java"});
    }
}
```


----------



## Sunchezz (5. Mai 2011)

ok, ich entschuldige mich mal für diverse unklarheiten^^

das entscheidene an meiner frage war eigentlich wie ich zur laufzeit eine klasse aus einem String interpretieren kann, die .class im ordner war eigentlich nur ein beispiel um zu verdeutlichen das ich den direken inhalt zur laufzeit in einem string habe... 
also vergesst den file, ich habe nur den bytecode der klasse in einem String.

Hilft mir also die Compiler Api weiter?
das ich zur laufzeit Kompilieren kann ist mir klar!
Aber ich habe ja bereits den bytecode...
Alsso bräuchte ich im prinzip nen interpreter, aber das is ja eigentlich schwachsinn weil der ja schon läuft, also muss ich nur aus dem bytecode nur eine geladene klasse machen...

(ich glaube ich sollte wieder ins bett gehen, doch schon etwas spät um solche komplexen sachverhalte irgendwie in verständlich sätze zu packen!)

Also gute nach und danke schonmal!

EDIT:
Ok, lesen fällt zu dieser Uhrzeit auch nich mehr so leicht xD
ich sehe die Antwort die ich wollte hab ich von Splinter schon bekommen, ich werde das morgen mal testen.
Danke


----------



## Murray (5. Mai 2011)

Wenn du den Bytecode in einem String hast, dann hilft dir Splinters Lösung ab Zeile 8. Das "bytecode"-Array muss eben nur nicht zuerst aus einer Datei gelesen werden, sondern kann mit getBytes() aus dem String entnommen werden.

Allerdings: bei Bytecode handelt es sich prinzipiell um irgendwelche Binärdaten; wenn man die in Strings wandelt und wieder zurück, dann könnte man Probleme bekommen, wenn man nicht in beide Richtungen das gleiche Encoding verwendet. Wie bekommst du denn den Bytecode in den String?


----------



## Gastredner (5. Mai 2011)

Murray hat gesagt.:


> Allerdings: bei Bytecode handelt es sich prinzipiell um irgendwelche Binärdaten; wenn man die in Strings wandelt und wieder zurück, dann könnte man Probleme bekommen, wenn man nicht in beide Richtungen das gleiche Encoding verwendet. Wie bekommst du denn den Bytecode in den String?


Über einen Reader, würde ich wetten.
@TO: Lese die Datei über einen Stream in ein [c]byte[][/c] ein verwende dann splinters Lösung zum Definieren der neuen Klasse.


----------



## Sunchezz (5. Mai 2011)

Also ich bin nun ein wenig verwirrt hierrüber:


```
public static void main (String[] args) {
    byte[] bytecode = s.getBytes(); //s ist der String in den ich einfach zum testen mal ganz dreist
                                                      //den inhalt der class datei kopiert habe
    ClassLoader classloader = ClassLoader.getSystemClassLoader();
    Class clazz = classloader.defineClass("HalloWelt", bytecode, 0, bytecode.length, null);
}
```

Wenn ich das nun Kompiliere:

```
Loader.java:15:30: cannot find symbol
symbol  : method defineClass(java.lang.String,byte[],int,int)
location: class java.lang.ClassLoader
    Class clazz = classloader.defineClass("HalloWelt", bytecode, 0, bytecode.length);
                             ^
1 error
```

Ein blick in die API zeigt:
protected  Class<?> 	|| defineClass(String name, byte[] b, int off, int len)  Converts an array of bytes into an instance of class Class.
(API: Java™ Platform Standard Ed. 6)
(Meine: jdk1.6.0_22)

Also was zur Hölle übersehe ich hier??
liegts daran das die Methode protected is?
Wenn ja, bin ich eigentlich gewohnt das mich der Compiler auf solche Schusselfehler hinweist...
Steh grad irgendwie aufm Schlauch!


----------



## Gast2 (5. Mai 2011)

Ja die Methode ist protected, die kannst du nicht aufrufen


----------



## Andi_CH (5. Mai 2011)

Sunchezz hat gesagt.:


> Also was zur Hölle übersehe ich hier??
> liegts daran das die Methode protected is?
> Wenn ja, bin ich eigentlich gewohnt das mich der Compiler auf solche Schusselfehler hinweist...



Das kann er nicht wenn du Reflection verwendest - woher soll der Compiler wissen was da zur Laufzeit gerade für ein class-File rumlliegt?


----------



## tfa (5. Mai 2011)

> Das kann er nicht wenn du Reflection verwendest - woher soll der Compiler wissen was da zur Laufzeit gerade für ein class-File rumlliegt?


Der Compiler weiß, dass defineClass protected ist - oder was meintest du?


----------



## Sunchezz (5. Mai 2011)

defineClass ist doch eine definierte Methode innerhalb der API, wenn ich das richtig sehe, hat das doch nix mit Reflections zu tun, wenn ich Reflections benutze, bekomm ich zur laufzeit dem Compiler unbekannte Klassen und anderes, aber der Compiler sagt mir ja gerade das er die Methode nicht finden kann...


----------



## Gast2 (5. Mai 2011)

ja definiert ist sie schon, aber sie ist protected.
das heißt du kannst auf die methode nur zugreifen wenn du
- in der klasse selbst bist
- im selben package
- oder in einer unterklasse bist


----------



## splinter (5. Mai 2011)

Sunchezz hat gesagt.:


> Also was zur Hölle übersehe ich hier??
> liegts daran das die Methode protected is?
> Wenn ja, bin ich eigentlich gewohnt das mich der Compiler auf solche Schusselfehler hinweist...
> Steh grad irgendwie aufm Schlauch!



Du musst deinen eigenen ClassLoader schreiben. Die Methode ist wohl protected damit nicht jeder Klassen in einen fremden ClassSpace laden kann wie er lustig ist. (Könnte man aber auch umgehen...)

Eigener ClassLoader:

```
public class MyClassLoader extends ClassLoader {

	public MyClassLoader() {
		super(MyClassLoader.class.getClassLoader());
	}

	public Class<?> defineClassFromArray(byte[] byteCode) {
		return defineClass(null, byteCode, 0, byteCode.length);
	}
}
```


----------



## Sunchezz (5. Mai 2011)

EikeB hat gesagt.:


> ja definiert ist sie schon, aber sie ist protected.
> das heißt du kannst auf die methode nur zugreifen wenn du
> - in der klasse selbst bist
> - im selben package
> - oder in einer unterklasse bist



Ja danke, aber das ist mir bekannt...
Der Compiler weist einen doch aber eigentlich darauf hin, wie dieses kleine Beispiel beweist:

```
JProgressBar bar = new JProgressBar();
bar.paintBorder(bar.getGraphics());
```


```
Loader.java:15:8: paintBorder(java.awt.Graphics) has protected access in javax.swing.JProgressBar
bar.paintBorder(bar.getGraphics());
```

Ich versuche einfach nur eine Methode aus der Api aufzurufen, und der Compiler sagt mir das sie nicht existiert!
Protected hin oder her!!!
Das ist es was mich stutzig macht!

Das ich das mit nem eigenen Classloader beheben könnte darauf bin ich auch schon gekommen, allerdings bin ich noch nicht dazu gekommen es zu testen!
Ich werde berichten^^


----------



## Spacerat (5. Mai 2011)

Also deine Fehlermeldung sagt dir nicht, dass die Methode nicht existiert. Sie sagt dir lediglich, dass ein Symbol nicht gefunden werden kann, was natürlich kein Wunder ist, da das geforderte Symbol in deine aktuelle Umgebung nicht publiziert wurde, weil, wie wir bereits wissen, die Methode *protected* ist. Natürlich sollte einen sogar schon die IDE im Vorfeld auf die Problematik hinweisen.


----------



## Sunchezz (6. Mai 2011)

Wie ich in meinem Beispiel dargestellt habe, tut meine IDE esja normaler Weise auch, also warum hier nicht?


----------

