dynamische erzeugung eines Objektes

BlubBlub

Bekanntes Mitglied
Hi,

wie schaffe ich es eine Klasse dynamisch zu erzeugen?
Ich beschreibe das Problem mal genauer:

Ich habe einen Ordner namens "External". In diesem Ordner
befinden sich beliebig viele java Klassen. Jede dieser Klassen
implementiert ein von mir erstelltes Interface "ExternalFunction".
Somit hat jede Klasse folgende Signatur:

Java:
public class CLASSNAME implements ExternalFunction

und das Interface sieht so aus:

Java:
public interface ExternalFunction
{
         public String doSomething();
         public String getDescription();
}

In meinem Hauptprogramm möchte ich nun von jeder Klasse die im Ordner
"External" vorhanden ist eine Instanz erzeugen.
Anschließend rufe ich die Funktionen auf die das Interface vorschreibt.

Das Hauptprogramm würde beispielsweise so aussehen:
Java:
public static void main(String[] args)
{
       ExternalFunction[] externalFunctions;
      
       int numberOfClassesInFolder = ...;
       for(int i=0; i < numberOfClassesInFolder; i++)
       { 
             //Hier liegt das Hauptproblem. Im Ordner "External" sind eine variierende Anzahl
             //an Klassen enthalten. Zudem weiß ich nicht welchen Klassennname die von
             //einem Programmierer dort eingefügte Klasse hat. Ich weiß lediglich, dass 
             //jede Klasse das Interface "ExternalFunction" implementiert.
             //CLASSNAME dient hier grad zu verdeutlichung meines Problems dort müsste
             //der Name der Klasse stehen, die der Programmierer erstellt und hinterlegt hat.
             //Es ist ja möglich den Dateinamen als String herauszulesen. Und der Dateiname
             //entspricht ja immer dem Klassennamen. 
             
             String CLASSNAME = ...;//programmatisch den Dateinamen holen 
             externalFunctions[i] = new CLASSNAME();     //wie kann ich dies umsetzen?       
       }

       for(int i=0; i < externalFunctions.length; i++)
       {
               externalFunctions[i].getDescription();
               externalFunctions[i].doSomething();
       }
}
 
Zuletzt bearbeitet:
T

Tomate_Salat

Gast
Java:
Class.forName(className).newInstance();
setzt aber voraus, dass ein Standard-konstruktor vorhanden ist.
 

faetzminator

Gesperrter Benutzer
Cool, wusste ich nicht. Meist verlink ich auf [c]javadocs.org/Klasse[/c], aber da dies irgendwie nicht funktionierte, hab ich umständlich in Google suchen müssen :(
 

langhaar!

Bekanntes Mitglied
wie schaffe ich es eine Klasse dynamisch zu erzeugen?

Du verwechselst Klassen und Objekte. Eine Klasse wird statisch programmiert. Eine Klasse dynamisch zu erzeugen... Nun, dazu müsste man Code generieren und diesen kompilieren. Du willst aber nur Objekte instanziieren. Wie das gehen kann, wurde ja bereits erwänht.

Weiterhin ist das Vorgehen mit einer Variable numberOfClassesInFolder sehr unschön. Willst du das Programm bei jeder neuen Klasse ändern?
 

BlubBlub

Bekanntes Mitglied
Ja da hat sich oben wohl der Fehlerteufel eingeschlichen natürlich muss da stehen:
"wie schaffe ich es ein Objekt dynamisch zu erzeugen?".

Die numberOfClassesInFolder Variable wird nicht hardcodiert, die wird selbstverständlich programmatisch vorher ermittelt.

So ich habe nun ein kleines Testprogramm geschrieben. Doch es tritt eine
ClassNotFoundException auf. Was habe ich falsch gemacht?

Java:
public class Starter
{
	public static void main(String[] args)
	{        
        String currentDirectoryPath = System.getProperty("user.dir");
        File file = new File(currentDirectoryPath, "bin/External/");
        String directoryPath = file.getAbsolutePath();
        System.out.println(directoryPath);
        String[] filenames = file.list();
        ExternalFunction[] externalFunctions = new ExternalFunction[filenames.length];
        
        for(int i=0; i<filenames.length; i++)
        {
        	System.out.println(filenames[i]);
        	String str = filenames[i].substring(0, filenames[i].length()-6);
        	String filePath = directoryPath + str;  //hier ist der absolute Pfad zur class Datei angegeben, wobei die Endung ".class" abgeschnitten wurde
        	System.out.println(filePath);
        	
        	try 
        	{
        		externalFunctions[i] = (ExternalFunction) Class.forName(filePath).newInstance();
			} 
        	catch (InstantiationException e)
        	{
				e.printStackTrace();
			} 
        	catch (IllegalAccessException e)
        	{
				e.printStackTrace();
			}
        	catch (ClassNotFoundException e)
        	{
				e.printStackTrace();
			}
        }

	 
        for(int i=0; i < externalFunctions.length; i++)
	    {
	    	externalFunctions[i].getDescription();
	        externalFunctions[i].doSomething();
	    }
	}
}

Auf der Console bekomme ich ausgegeben:
C:\Users\Benutzer\workspace\ExternalClass\bin\External

ExternalClassOne.class
C:\Users\Benutzer\workspace\ExternalClass\bin\ExternalExternalClassOne
java.lang.ClassNotFoundException: C:\Users\Benutzer\workspace\ExternalClass\bin\ExternalExternalClassOne

ExternalClassTwo.class
C:\Users\Benutzer\workspace\ExternalClass\bin\ExternalExternalClassTwo
java.lang.ClassNotFoundException: C:\Users\Benutzer\workspace\ExternalClass\bin\ExternalExternalClassTwo




in dem External Ordner befinden sich die beiden class Dateien ExternClassOne und ExternalClassTwo.
 
Zuletzt bearbeitet:
T

Tomate_Salat

Gast
Beispiel für ein Class.forName:
Java:
Object someObject=Class.forName("java.lang.Object").newInstance();
du gibst hier nicht den Pfad an, sondern das Paket+Klassennamen. Wenn du eine externe Klasse laden willst, musst du wie von Faetzminator schon beschrieben, die klasse erst über den [japi]URLClassloader[/japi] laden.
 

faetzminator

Gesperrter Benutzer
Verwend einen [japi]URLClassloader[/japi], welchem du den Pfad zum Ordner mitgibst. Mit diesem Classloader kannst du dann z.B. [c]ExternalExternalClassTwo[/c] laden.
 

BlubBlub

Bekanntes Mitglied
Okay ich hab doch noch nicht ganz verstanden wie das genau funktioniert.

Also angenommen ich habe ein java Programm geschrieben namens "FunnyFunctions".

Normalerweise hat man ja als Endanwender auf einem Windows Rechner auf seinem Desktop
ein Icon mit der Beschriftung "FunnyFunctions.exe". Wenn man einen Doppelklick
auf dieses Icon macht dann wird das FunnyFunction Javaprogramm gestartet.
Wie erstellt man denn so eine ".exe" Datei?.
Ich stelle mir das momentan so vor, dass der Doppelklick auf die ".exe" Datei irgendwie dazu führt
dass die "FunnyFunctions.class" Datei ausgeführt wird. Denn aus der ".java" Datei wurde nach dem
Kompilieren ja eine ".class" Datei erzeugt. Und wenn man nun das Programm öfter mal
ausführt so wird immer nur die ".class" Datei zur Ausführung des Programm verwendet. Im Prinzip
könnte man ja die ".java" Datei sogar vollständig löschen. So stell ich mir das zumindest vor.


Ich möchte nun, dass es ermöglich wird dem Programm "FunnyFunctions" beliebige weitere Funktionen hinzuzufügen,
ohne, dass ich das Hauptprogramm neu kompilieren muss mit den neuen ".java" Dateien.
Das einzige was der Benutzer vor dem Start des Programms "FunnyFunctions" nur machen sollen muss, ist seine selbstgeschrieben Klasse
welche ein bestimmtes Interface implementieren muss, in einen fest vorgeschriebenen Ordner zu legen.
Anschließen soll die Funktion dem Programm zur Verfügung stehen.

Seh ich das richtig dass lediglich eine ".class" Datei in diesen festvorgeschrieben Ordner gelegt werden muss?
Oder muss da die ".java" Datei hingelegt werden?

Class.forName(String name). Greift diese Funtkion auf die ".class" oder ".java" Datei zu? Auf ersteres oder?
 

BlubBlub

Bekanntes Mitglied
Nagut ich seh schon, dass ist ein etwas komplexeres Thema eine plattformunabhängige
"exe" Datei zu erstellen oder wie man auch immer diese Dateien bezeichnen mag.
Dies ist auch erstmal nicht so wichtig, das hat mich nur nebenher interessiert.
Viel wichtiger ist folgendes. Wenn ich in Eclipse ein Projekt habe. Und dort über den
Run button mein Programm starte, so greift Eclipse, wenn das Programm nicht gerade
das erste mal startet ja immer auf die class Dateien zurück, die beim allerersten
Start erstellt wurden.


Meine Programmstruktur in Eclipse sieht so aus:
Projekt
-src
|-----Exceptions (package)
| |-----------------------NoExternalFunctionInterfaceException.java
|
|-----External (package)
| |-----------------------ExternalClassOne.java
| |-----------------------ExternalClassTwo.java
| |-----------------------ExternalClassThree.java
|
|------GUI (package)
|----------------------MyMain.java

Schaut man im workspace Verzeichnis welches Eclipse verwendet, so befindet sich dort
sowohl ein src Verzeichnis als auch ein bin Verzeichnis.
Im src Verzeichnis sind die ".java" Datei und im ".bin" Verzeichnis befinden sich die
dazugehörigen ".class" Dateien.

Die drei aufgelisteten Dateien "ExternalClassOne.java" , "ExternalClassTwo.java" und "ExternalClassThree.java" sind die von jemanden dritten geschriebenen Dateien
die eingebunden werden sollen können, ohne das eigentliche Programm mit diesen
Dateien neu kompileren zu müssen. Dies ist ein Grund weshalb ich class.forName() verwende
um damit Objekte dieser Klassen dynamisch erzeugen zu können.

Ich hab mir jetzt gedacht, dass die neuen externen Klassen vor dem Start des Programms
erst mal immer in das External Verzeichnis abgelegt werden müssen.
Dabei habe ich versucht nur die ".class" Datei welche ich in den Ordner "External" der im "bin" Verzeichnis drin ist zu hinterlegen. Den Ordner "External" im "src" Verzeichnis habe ich NICHT mit
der entsprechenden ".java" Datei gefüllt.
Starte ich nun mein Programm, so erreiche ich mein gewünschtes Ziel nicht, dass eine neue exteren Klasse
mit einer bestimmte Funktion zur Verfügung steht.
Wenn ich die entsprechende ".java" Datei nun auch in den "src" Ordner lege, funktioniert das ganze ebenfalls nicht.

Nur wenn ich die java Dateien in Eclipse in das package einfüge und dann zusammen mit dem Programm kompiliere erreiche ich den erwünschten Effekt dass eine neue externe Klasse mit einer bestimmten
Funtkion zur Verfügung steht. Aber das ist ein recht umfangreicher Weg, wie ich finde.

Was mache ich denn da falsch? Irgendwie wird mir der Fehler nicht ganz klar.
Mit class.forName() kann ich zwar eine Klasse dynamisch dazu holen, aber aus irgendeinem
Grunde muss ich die gewünschten Klassen zusammen mit dem Hauptprogramm kompilieren,
welche ich im package External hinterlege. Jedoch möchte ich ein erneutes kompilieren des
Hauptprogramms verhindern.

Java:
package GUI;

import java.io.File;

import Exceptions.NoExternalFunctionInterfaceException;
import Interfaces.ExternalFunction;

public class ExternalFunctionManager 
{
	private ExternalFunction[] externalFunctions;
	private String[] classNames;
	
	public ExternalFunctionManager()
	{
		  File file = new File(System.getProperty("user.dir"), "bin/External");
		  String[] filenames = file.list();
		  classNames = new String[filenames.length];
		  externalFunctions = new ExternalFunction[filenames.length];
		    
		  for(int i=0; i<filenames.length; i++)
		  {
			  String packageName = "External";	  
			  classNames[i] = filenames[i].substring(0, filenames[i].length()-6);
		    	
			  try 
			  {
                             
				  Object clazz = Class.forName(packageName + "." + classNames[i]).newInstance();
				  if(clazz instanceof ExternalFunction)
		    			externalFunctions[i] = (ExternalFunction)clazz;
				  else throw new NoExternalFunctionInterfaceException(filenames[i]);
			  } 
			  catch (InstantiationException e)
			  {
				  e.printStackTrace();
			  } 
			  catch (IllegalAccessException e)
			  {
				  e.printStackTrace();
			  }
			  catch (ClassNotFoundException e)
			  {
				  e.printStackTrace();
			  }
			  catch (NoExternalFunctionInterfaceException e)
			  {
				  System.out.println("Remove the binary file " + e.getMessage() + " of the directory \n" + System.getProperty("user.dir") + "\\bin\\External");
				  e.printStackTrace();
				  System.exit(1);
			  } 
		   }
	}
	
	
	public ExternalFunction[] getExternalFunctions()
	{
		return this.externalFunctions;
	}
	
	public String[] getClassNames()
	{
		return this.classNames;
	}
	
	public void callExternalFunctions(String callerName)
	{
	    for(int i=0; i < externalFunctions.length; i++)
	    {
	    	externalFunctions[i].getDescription();
	        externalFunctions[i].doSomething();
	    }	
	}
}
 

Michael...

Top Contributor
Das einzig entscheidende ob eine Klasse geladen werden kann oder nicht ist, dass eine Klasse und die durch sie referenzierten Klassen über den CLASSPATH zu finden sind.
D.h. das Verzeichnis oder JAR in dem sich die Klassen befinden, muss im CLASSPATH gelistet sein.
Aber Achtung wenn Du einzelne class Files (nach über 240 Posts sollte man wissen, dass es nur um diese geht und die java Dateien "nur" den Quellcode zur Erzeugung enthalten) die eventuell in Packages strukturiert sind lose in ein Verzeichnis ablegst, muss die Package Hierarchie als Verzeichnis abgebildet werden.

Beispiel:
Klasse
Code:
de.package.Test
liegt in Dateiform als
Code:
Test.class
vor.
Code:
C:\MeinProgramm\ExtLibs
ist das Verzeichnis in dem die externen Klassen abgelegt werden sollen und wird so im CLASSPATH aufgeführt.
Dann muss der Verzeichnis Pfad zum class -File wie folgt ausschauen:
Code:
C:\MeinProgramm\ExtLibs\de\package\Test.class

Zum Laden muss natürlich der gesamte Name der Klasse (!=Dateiname) angegeben werden:
Code:
Class.forName("de.package.Test");
 
Zuletzt bearbeitet:

BlubBlub

Bekanntes Mitglied
Aber muss ich in dem folgenden Fall auch Änderungen im CLASSPATH oder so vornehmen?
Denn folgendes ist mir nicht klar:
Die Klassen die von einem Dritten erstellt werden und ein bestimmtes Interface implementieren,
werden von dem Dritten in den Ordner "External" gelegt.
Der einfachheit halber sag ich jetzt mal, die Person die eine neue Klasse erstellt hat kompiliert sie und erhält daraus die ".class" Datei. Die ".java" Datei löscht er danach komplett.
Diese ".class" Datei muss er immer selbst von Hand in den Ordner "External" legen. Das setzt ich jetzt einfach voraus.
Als Programmierer habe ich vorher dafür gesorgt, dass es diesen Ordner auch gibt, indem ich einfach
ein leeres Package namens "External" in Eclipse in meinem Projekt erstellt habe.
Schaut man im workspace nun nach so befindet sich der Ordner "External" als Unterornder einmal
im "src" Ordner wo die ganzen ".java" Dateien liegen und einmal im "bin" Ordner wo die ganzen ".class" Dateien liegen. Jeder Dritte der eine eigene Klasse erstellt legt NUR die ".class" Datei ab und zwar in dem Unterordner "External" der im Ordner "bin" vorhanden ist.
Es liegt also eine kompilierte Version der Klasse des Dritten vor.
Ich nenne jetzt diese neue Klasse einfach mal "ThirdClass".

Somit sieht es im workspace folgendermassen aus:
\workspace\MyProject\src\External\
\workspace\MyProject\bin\External\ThirdClass.class

Warum funktioniert also folgendes nicht?
Java:
  Object clazz = Class.forName("External.ThirdClass").newInstance();
ExternalFunction externalFunction = (ExternalFunction)clazz;
externalFunction.doSomething();

Dies funktioniert nur wenn der Dritte Eclipse öffnent und dann in das Package External
die Klasse "ThirdClass.java" einfügt und dann über den "Run" Button das Programm startet.
Dann kann ich in meiner GUI auf die Funktionen der "ThirdClass" Klasse zugreifen.

Der Punkt den ich also nicht verstehe ist, warum funktioniert das Programm
,wenn ich die ThirdKlasse.java im Eclipseeditor in das Package "Externel" kopiere und anschließen auf den Run Button klicke problemlos

ABER nicht wenn ich einfach NUR die ThirdKlasse.class Datei von Hand zuerst in
\workspace\MyProject\bin\External\ThirdClass.class
ablege und dann das Programm über den Run Button kompiliere und starte.
 

BlubBlub

Bekanntes Mitglied
So ich hab den Fehler jetzt gefunden.

Also angenommen die Klasse "ThirdClass.java" sieht so aus:

Java:
// In dieser ersten Zeile lag der Fehler. Die ThirdClass hatte ich im package ClassMaker erstellt.
// Damit aber die ".class" Datei später im "External" Verzeichnis funktioniert 
// muss da package External stehen. 
// Leider bedeutet dies das jeder der eine eigene Klasse schreibt auch immer erst in 
// Eclipse ein package External erstellen muss und dort die Klasse kompilieren muss.
// Frage: Wie setzt man es um, dass die Datei in einem beliebigen package erstellt und kompiliert
// werden kann und dass die ".class" Datei später dennoch in das "External" Verzeichnis des 
// Hauptprogramm abgelegt werden kann.
package External;  

import Interfaces.ExternalFunction;

public class ThirdClass implements ExternalFunction
{
	public ThirdClass(){}

	@Override
	public String doSomething()
	{
		System.out.println("ThirdClass doSomething");
		return null;
	}

	@Override
	public String getDescription() 
	{
		String description = "ThirdClass Description";
		
		return description;
	}
}

Wenn diese Klasse nun kompiliert wird und ich somit daraus eine ".class" Datei erzeugt habe
kann ich diese class Datei problemlos so ablegen.

\workspace\MyProject\bin\External\ThirdClass.class

Somit müsste das spätere Programm auch nicht jedes mal neu kompiliert werden mit der neuen Klasse.
Es genügt einfach ThirdClass.class so in das Verzeichnis abzulegen und das Programm zu starten.
Nehm ich jetzt zumindest stark an.

Der Fehler der bei mir auftrat war, dass die ThirdClass.java Datei in einem andere package beim Kompilieren lag. Somit stand in der ersten Zeile der java Datei nicht.

Java:
package External;
sondern
Java:
package ClassMaker;

Die erzeugte ".class" Datei lege ich aber später in das Verzeichnis "External". Daher auch der Fehler.


Aber nun habe ich ein neues Dilema.
Nun muss ich jedem Dritten vorschreiben, dass um eine zu akzeptierende ".class" Datei zu erzeugen,
er in seiner Eclipse Entwicklungsumgebung erst ein package "External" angelegen muss, wo dann dann Klasse geschrieben und anschließend kompiliert wird.

Wie kann man das eleganter lösen, so dass nicht jeder Dritte so ein "External" package erst erstellen muss, damit in der .class Datei "package External" vorhanden ist.
 

Michael...

Top Contributor
Wie bereits gesagt, das Verzeichnis, unter dem die Klassen liegen muss in den CLASSPATH. Dein External "Verzeichnis" in Eclipse ist kein Verzeichnis sondern ein Package, das bitte beachten und nicht durcheinander bringen.
Bin mir gerade nicht ganz sicher, aber Eclipse bietet unter den "Run Configurations" (oder so ähnlich) die Möglichkeit externe Verzeichnis, Archive usw. in den CLASSPATH aufzunehmen (gilt aber nur innerhalb von Eclipse) hier könntest Du ein beliebiges Verzeichnis auf der Festplatte auswählen, welches Du testweise Deine "Plugin" Klassen legen kannst - aber: Packagestruktur dieser Klassen beachten.
 

BlubBlub

Bekanntes Mitglied
Okay, danke für die Hilfe.
Puh ist gar nicht mal so einfach, die Sache mit
dem Classpath und dem Einbinden neuer class Dateien zu verstehen.
Ich werde mich in den nächsten Tagen noch ein wenig weiter damit
beschäftigen. Langsam versteh ich ein klein wenig das Grundvorgehen.
Muss das aber noch ein wenig durchtesten.

Für alle die es interessieren sollte.
Hab noch ne recht gute Seite dazu gefunden:

einbinden-von-externen-klassen-classpath/
 

faetzminator

Gesperrter Benutzer
Lass die externen Files doch einfach ohne Package oder schiebe sie in [c]External/external/[/c] (und nicht External, Packages schreibt man klein!). Danach musst du - wie von div. Usern bereits gesagt - einfach den Ordner [c]External/[/c] dem URLClassLoader bekannt machen, und du kannst die Klasse laden. Je nach dem, ob in Package oder nicht, mit [c]external.FooBar[/c] oder [c]FooBar[/c].
 

BlubBlub

Bekanntes Mitglied
Hmm ich hab noch ein wenig über den CLASSPATH nachgedacht.
Irgendwie scheint mir das mit dem CLASSPATH setzen nicht die optimale Lösung zu sein,
wenn ich ein Hauptprogramm namens "FunnyFunctions" habe und möchte, dass ein
Dritter eine eigene neue Funktion (die in einer Klasse enthalten ist) mittels einer ".class" Datei dem Programm zur
Verfügung stellen will ohne das Hauptprogramm neu kompilieren zu müssen.

Denn laut dem Link einbinden-von-externen-klassen-classpath/ unter dem Abschnitt "Den Classpath für ein Programm setzen" werden dazu zwei Möglichkeiten vorgeschlagen.

Die erste wäre eine Umgebungsvariable CLASSPATH zu setzen, was jedoch meines Achtens viel zu umständlich ist.

Die zweite Möglichkeit die dort aufgelistet ist, sagt dass man beim Compilieren den CLASSPATH setzen muss und dann nochmal beim starten des Programms. ["Der weitaus schönere, wenn auch aufwendigere Weg, ist das direkte Setzen des Classpath beim Kompilieren und später auch beim Ausführen eines Programms"]

Aber das ist meines Achtens auch genauso umständlich.

1.Frage: kann man den CLASSPATH nur so setzen? Kann man nicht in einer Java Datei also direkt in der neuen Klasse zu Beginn irgendwie
einen Befehl programmieren a la "System.setClasspath();" oder sowas in der Art, der beim ausführen der Klasse abgearbeiten wird?
Wenn ich mir ein Programm kaufe, welches plugins anbietet, dann wirkt das irgendwie
sehr kompliziert, wenn man um die neuen Plugins verwenden zu können, selbst als Benutzer erst
den CLASSPATH richtig setzen müsste. Ich nehme mal an, dass dort andere Mechanismen verwenden
werden, allerdings ist mir dann nicht ganz klar warum man überhaupt jemals sich dafür entscheiden sollte diesen aufwändigen Weg über CLASSPATH für irgendetwas zu beschreiten. Doch irgendwelche
Gründe muss es ja dafür geben, dass man sich manchmal doch eben für das setzen des CLASSPATHs
entscheidet.


Ich stelle mir die optimale Lösung so vor. Man hat eine exe Datei oder sowas ähnliches mit dem man das Hauptprogramm "FunnyFunctions" startet.
Neben dieser exe Datei gibt es einen Ordner "External". Dieser Ordner muss sich im selben Verzeichnis
befinden wie die exe Datei mit allen seinen anderen zugehörigen Dateien. Der Ordner "External" muss sich deshalb im selben Verzeichnis befinden, da ich den Pfad zu dem Ordner ja nicht hardcodieren kann. Somit muss ich also mittels eines relativen Pfades auf den Ordner vom Hauptprogramm aus zu greifen, der die externen Klasse dazulädt. In diesen "External" Ordner kommen vor dem ausführen des "FunnyFunction" Programms die von einem Dritten programmierten ".class" Dateien, die anschließden dem Programm zu Verfügung stehen.
Damit wäre es ganz einfach für einen Dritten eigene Funktionen (bzw. die Klassen) dem Programm hinzuzufügen. Er müsste nur seine ".class" Datei in den Ordner einfügen, was denke ich für jeden Anwender einfach wäre.

Also wie ihr mir hier geschrieben habt sollte ich einen URLClassLoader verwenden. Also verstehe ich das richtig, dass wenn ich den URLClassLoader verwende ich keinen Classpath setzen muss und die grad beschriebene gewünschte Funktionsweise erreiche? Dem URLClassLoader würde ich ja dann den relativen Pfad zu dem Ordner "External" geben und dann den entsprechende Klassennamen der ".class" Datei um so dynamisch ein Objekt der Klasse zu erzeugen die ich so anspreche.
Hab ich das richtig verstanden?
 
T

Tomate_Salat

Gast
Richtig, mit dem URLClassLoader kannst du einfach deine Daten dazuladen. Du musst nur aufpassen, wo und wie du anfängst zu laden. Bei *.jar-Files hat man wenig zu beachten. Wenn die richtig gepackt wurden, hast du deine gewünschte Ordnerstruktur. Wenn du aber *.class-Files rumliegen hast, musst du darauf achten, dem URLClassLoader den richtigen Pfad zu geben. Wenn eine *.class als Packageangabe hat: [c]package de.yourcompany;[/c], dann muss der URLClassloader von einem Verzeichnis aus anfangen zu laden, welches folgende Struktur hat:
Code:
|- de
|   |-yourcompany
|   |        '-YourClass.class
oder kurz: [c]./de/yourcompany/YourClass[/c]. Das ist wichtig, weil du beim laden der Klasse auch die Pakete mit angibst. Dementsprechend sucht er die Ordner durch und lädt (wenn er sie denn Findet) deine *.class. Also eine Pfadangabe die direkt auf die *.class verweist, würde in diesem Falle zu einer Exception führen. (Letzendlich das gleiche Prinzip wie bei javac, dort arbeitest du auch ausgehend vom root-verzeichnis).
 

BlubBlub

Bekanntes Mitglied
Also ich hab grad folgendes ausprobiert:

Auf dem Desktop habe ich ein Verzeichnis MyExternalDirectory angelegt.
Da rein habe ich eine ".class" Datei eingefügt namens "MyExternalClass.class".
C:\Users\Ich\Desktop\MyExternalDirectory\MyExternalClass.class

Starte ich Eclipse so befindet sich dort ein Projekt namens MyProject.
Darin sind zwei ".java" Dateien enthalten.
Einmal die "Starter.java" Datei welches die main Methode beinhaltet.
Und einmal die "ExternalFunction.java" welche das Interface "ExternalFunction" darstellt.

Der Projektaufbau sieht somit so aus:

Code:
MyProject
|-------------src
                   |------(default package)
                                     |-----------------ExternalFunction.java
                                     |-----------------Starter.java


Und hier der Programm Code:

Starter.java
Java:
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;


public class Starter 
{
	public static void main(String[] args)
	{
		try 
		{			  
			URL urls = new URL("file:\\\\\\C:\\Users\\Ich\\Desktop\\MyExternalDirectory\\");
			URL[] classpath = {urls};
			URLClassLoader urlClassLoader = new URLClassLoader(classpath);
			Class<?> clazz = urlClassLoader.loadClass("MyExternalClass");
			Object object = clazz.newInstance();
			ExternalFunction externalFunction = (ExternalFunction)object;
			externalFunction.doSomething();
		}
		catch (MalformedURLException e)
		{
			e.printStackTrace();
		} 
		catch (ClassNotFoundException e)
		{
			e.printStackTrace();
		}
		catch (InstantiationException e)
		{
			e.printStackTrace();
		} 
		catch (IllegalAccessException e)
		{
			e.printStackTrace();
		}
	}
}

ExternalFunction.java
Java:
public interface ExternalFunction 
{
    public String doSomething();
    public String getDescription();
}

MyExternalClass.java
Java:
public class MyExternalClass implements ExternalFunction
{
	public MyExternalClass(){}
	 
    @Override
    public String doSomething()
    {
        System.out.println("MyExternalClass doSomething");
        return null;
    }
 
    @Override
    public String getDescription() 
    {
        String description = "MyExternalClass Description";
        return description;
    }
}

Starte ich das Programm so wird wie gewünscht: "MyExternalClass doSomething" auf der Konsole
ausgegeben.


Nun wollte die neue Funktion die ich über die neu eingebundene Klasse erhalte in meinem
eigentlichen Programm ausprobieren.
Mein eigentliches Programm ist bereits ziemlich groß, sicher bestehend aus mindestens 5000 Zeilen
Code und geschätzten 30 packages.
Leider bekomme ich da beim starten des Programms folgende Fehlermeldung:

Code:
Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: ExternalFunction
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClassCond(Unknown Source)
	at java.lang.ClassLoader.defineClass(Unknown Source)
	at java.security.SecureClassLoader.defineClass(Unknown Source)
	at java.net.URLClassLoader.defineClass(Unknown Source)
	at java.net.URLClassLoader.access$000(Unknown Source)
	at java.net.URLClassLoader$1.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at graphicalGUI.functionSettingsPanel.ExternalFunctionManager.<init>(ExternalFunctionManager.java:36)
	at graphicalGUI.GraphicAnimator.<init>(GraphicAnimator.java:142)
	at simulationTool.inputGUI.InputGUIMenubar$10.actionPerformed(InputGUIMenubar.java:613)
	at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
	at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
	at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
	at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
	at javax.swing.AbstractButton.doClick(Unknown Source)
	at javax.swing.plaf.basic.BasicMenuItemUI.doClick(Unknown Source)
	at javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(Unknown Source)
	at java.awt.Component.processMouseEvent(Unknown Source)
	at javax.swing.JComponent.processMouseEvent(Unknown Source)
	at java.awt.Component.processEvent(Unknown Source)
	at java.awt.Container.processEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Window.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: ExternalFunction
	at java.net.URLClassLoader$1.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	... 41 more

Ich habe die Vermutung der Fehler liegt daran, dass die MyExternalClass.class Datei
keine package Angabe beinhaltet, wo sich das Interface "ExternalFunction" in meinem
eigentlichen Projekt befindet.

Vermutlich hat das Programm in MyProject problemlos funktioniert, da es dort keine packages
gibt und das Interface "ExternalFunction" im selben Verzeichnis liegt wie die Klasse "Starter".

Bei meinem eigentlichen Projekt gibt es allerdings zahlreiche packages.
Ein Ausschnitt der Ordnerstruktur sieht so aus:

Code:
SimulationTool3
  |---------------------editor
                                   |-------------graphicalGUI
                                   |                        |---------------GraphicAnimator.java
                                   |
                                   |-------------graphicalGUI.functionSettingsPanel
                                   |                        |----------------ExternalFunctionManager.java
                                   |
                                   |-------------graphicalGUI.functionSettingsPanel.interfaces
                                                            |----------------ExternalFunction



Der Programmablauf sieht in etwa so aus.
Zuerst wird die Klasse GraphicAnimator gestartet.

Java:
public class GraphicAnimator extends JFrame implements IOutputGUI
{	
	public GraphicAnimator(ISimulator iSimulator)
	{
		super();
	     
                ...
                ...
                ...
	       
                this.externalFunctionManager = new ExternalFunctionManager();

               ...
               ...
               ...
        }
}

Java:
package graphicalGUI.functionSettingsPanel;

import graphicalGUI.functionSettingsPanel.Exceptions.NoExternalFunctionInterfaceException;
import graphicalGUI.functionSettingsPanel.interfaces.ExternalFunction;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class ExternalFunctionManager
{
	private ExternalFunction[] externalFunctions;
	private String[] classNames;
	
	public ExternalFunctionManager()
	{
		  File file = new File("C:\\Users\\Ich\\Desktop\\MyExternalDirectory\\");
		  String[] filenames = file.list();
		  classNames = new String[filenames.length];
		  externalFunctions = new ExternalFunction[filenames.length];
		    
		  for(int i=0; i<filenames.length; i++)
		  {
			  classNames[i] = filenames[i].substring(0, filenames[i].length()-6);
		    	
			  try 
			  {	  
				  URL urls = new URL("file:\\\\\\C:\\Users\\Ich\\Desktop\\MyExternalDirectory\\");
				  URL[] classpath = {urls};
				  URLClassLoader urlClassLoader = new URLClassLoader(classpath);
				  Class<?> clazz = urlClassLoader.loadClass("MyExternalClass"); //Beim Debuggen kann ich sehen, dass hier die Exception geworfen wird
				  Object object = clazz.newInstance();
				  
				  if(object instanceof ExternalFunction)
		    			externalFunctions[i] = (ExternalFunction)object;
				  else throw new NoExternalFunctionInterfaceException(filenames[i]);
			  } 
			  catch (InstantiationException e)
			  {
				  e.printStackTrace();
			  } 
			  catch (IllegalAccessException e)
			  {
				  e.printStackTrace();
			  }
			  catch (ClassNotFoundException e)
			  {
				  e.printStackTrace();
			  }
			  catch (NoExternalFunctionInterfaceException e)
			  {
				  System.out.println("Remove the binary file " + e.getMessage() + " of the directory \n C:\\Users\\Pj\\Desktop\\MyExternalDirectory\\");
				  e.printStackTrace();
				  System.exit(1);
			  }
			catch (MalformedURLException e)
			{
				e.printStackTrace();
			} 
		   }
	}
	
	
	public ExternalFunction[] getExternalFunctions()
	{
		return this.externalFunctions;
	}
	
	public String[] getClassNames()
	{
		return this.classNames;
	}
	
	public void callExternalFunctions(String callerName)
	{
	    for(int i=0; i < externalFunctions.length; i++)
	    {
	    	externalFunctions[i].getDescription();
	        externalFunctions[i].doSomething();
	    }	
	}
}

Java:
package graphicalGUI.functionSettingsPanel.interfaces;

public interface ExternalFunction
{
    public String doSomething();
    public String getDescription();
}

Wie man sieht unterscheidet sich die Klasse ExternalFunctionManager in meinem Hauptprojekt kaum von der Klasse in dem kleinen Testprojekt namens "MyProject". Allerdings funtkioniert sie im Hauptprojekt nicht und im "MyProject" läuft alles wie gewünscht.
Während auf der Konsole beim Ausführen des Programm die obige Fehlermeldung mit
"Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: ExternalFunction"
geworfen wird, erfolgt bei "MyProject" die korrekte Konsolenausgabe ""MyExternalClass doSomething".

Meine Vermutung ist wie bereits erwähnt, dass der Fehler dadurch Verursacht wird,
dass im MyExternalClass.java Code bzw in der dazugehörigen ".class" Datei keine Angabe vorhanden ist
wo sich das Interface "ExternalFunction" im Hauptprojekt befindet.
Ich vermute der externen Class Datei müsste irgendwie dies mitgeteilt werden "import graphicalGUI.functionSettingsPanel.interfaces.ExternalFunction;".

Beim Debuggen des Hauptprojektes kann ich sehen, dass hier die Exception geworfen wird:
Java:
				  Class<?> clazz = urlClassLoader.loadClass("MyExternalClass");

Welche Vermutung habt ihr. Wie sehe euer Lösungvorschlag aus? Denn momentan weiß ich nicht
wie ich den Fehler beheben kann.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
ohneInformatik; Dynamische Zinsen. Wo liegt der Fehler? Java Basics - Anfänger-Themen 4
A Erste Schritte Dynamische Stempel im PDF Exchange programmieren Java Basics - Anfänger-Themen 0
B Fibonacci Zahlen dynamische Programmierung Java Basics - Anfänger-Themen 7
M Fehlendes Verständnis für dynamische Bindung und Vererbung Java Basics - Anfänger-Themen 13
L Dynamische Anzahl an Arrays mit verschiedenen Namen erzeugen Java Basics - Anfänger-Themen 6
L Dynamische Bindung Java Basics - Anfänger-Themen 3
W OOP Definition / Abgrenzung dynamische Bindung Java Basics - Anfänger-Themen 11
J Dynamische Datenstrukturen Java Basics - Anfänger-Themen 0
L Variablen Dynamische Variablenname Java Basics - Anfänger-Themen 9
L Dynamische Programmierung Java Basics - Anfänger-Themen 0
M Schlüsselworte Dynamische Polymorhpie Java Basics - Anfänger-Themen 32
J OOP Dynamische Objektnamen Java Basics - Anfänger-Themen 6
Ste3et_C0st Dynamische While/For Schleife Java Basics - Anfänger-Themen 7
F Erste Schritte Dynamische Variablen Java Basics - Anfänger-Themen 15
M Dynamische Methode aus anderer Klasse aufrufen Java Basics - Anfänger-Themen 11
S Dynamische Variable ist? Java Basics - Anfänger-Themen 11
S Verwirrung - Dynamische Bindung greift nicht Java Basics - Anfänger-Themen 2
C Dynamische Referenz & abstrakte Klassen Java Basics - Anfänger-Themen 3
P Klassen statische oder dynamische(?) Klasse Java Basics - Anfänger-Themen 3
J Dynamische Liste durchsuchen + anpassen Java Basics - Anfänger-Themen 3
A Schlüsselworte dynamische Stringteilung Java Basics - Anfänger-Themen 4
C Dynamische (AJAX) Inhalte einer Webseite mittels Java auslesen Java Basics - Anfänger-Themen 2
W Übungsaufgabe:Dynamische Datenstrukturen Java Basics - Anfänger-Themen 10
L Dynamische Objektgenerierung Java Basics - Anfänger-Themen 4
K Dynamische Bindungsregel Java Basics - Anfänger-Themen 2
B dynamische/statische Typen Java Basics - Anfänger-Themen 2
C dynamische JTextFields durchlaufen Java Basics - Anfänger-Themen 5
H Dynamische Bindung mit Interfaces und LinkedList Java Basics - Anfänger-Themen 7
N OOP Dynamische Objekte und nach Parametern durchsuchen Java Basics - Anfänger-Themen 4
M dynamische JPanels/Component Java Basics - Anfänger-Themen 3
X dynamische Listen Java Basics - Anfänger-Themen 2
A Schnelle, dynamische, geordnete Datenstruktur? Java Basics - Anfänger-Themen 11
M Dynamische JButtons mit ActionListener Java Basics - Anfänger-Themen 7
Y Kleine Verständnisfrage zum Thema dynamische Polymorphie Java Basics - Anfänger-Themen 3
C Dynamische Matrizen Java Basics - Anfänger-Themen 4
0 Dynamische Datenstruktur ohne Duplikate und mit direkter Elementauswahl Java Basics - Anfänger-Themen 3
N Vererbung/Dynamische Bindungen Java Basics - Anfänger-Themen 15
W Dynamische Bindung Java Basics - Anfänger-Themen 3
P jsp tags und scriplets mischen dynamische werte an jsp tag Java Basics - Anfänger-Themen 2
S Dynamische Tabelle Java Basics - Anfänger-Themen 2
P Suche Ersatz für dynamische arrays Java Basics - Anfänger-Themen 2
T Dynamische Reaktionen Java Basics - Anfänger-Themen 29
P Dynamische Bindung Java Basics - Anfänger-Themen 8
F Dynamische Speicheranpassung und exe Java Basics - Anfänger-Themen 9
D Dynamische Objektnamen / Variablen als Objektnamen verwenden Java Basics - Anfänger-Themen 3
J dynamische Auswahl einer überladenen Methode Java Basics - Anfänger-Themen 5
C JTable und dynamische Speicherung Java Basics - Anfänger-Themen 2
M Dynamische Wertsetzung von Variablen durch Eingaben Java Basics - Anfänger-Themen 9
J Dynamische Größenveränderung der Komponenten verhindern Java Basics - Anfänger-Themen 8
C Dynamische Operatoren! Java Basics - Anfänger-Themen 5
R dynamische Variablennamen Java Basics - Anfänger-Themen 3
M dynamische, assziative Arrays Java Basics - Anfänger-Themen 2
I dynamische mehrdimensionales Array Java Basics - Anfänger-Themen 8
H Unterschied statischer/dynamische Typ einer Variablen Java Basics - Anfänger-Themen 2
H statische,dynamische Bindung Java Basics - Anfänger-Themen 4
0 Dynamische Speicherverwaltung Java Basics - Anfänger-Themen 4
B Dynamische If Anweisung Java Basics - Anfänger-Themen 13
B Dynamische Variable Java Basics - Anfänger-Themen 12
C Dynamische Arraygröße Java Basics - Anfänger-Themen 2
M dynamische tabellen Java Basics - Anfänger-Themen 2
G Java dynamische Arrays?? Java Basics - Anfänger-Themen 2
Curtis_MC Erzeugung mehrdimensionaler Arrays Java Basics - Anfänger-Themen 2
J get methoden zugriff und objekt erzeugung Java Basics - Anfänger-Themen 30
O Java Erzeugung von neuen Objekten Java Basics - Anfänger-Themen 1
G Automatische Erzeugung von GUI Java Basics - Anfänger-Themen 3
R Interface Datentyp bei Erzeugung eines Objekts, dessen Klasse eine Schnittstelle implementiert Java Basics - Anfänger-Themen 18
J Erzeugung eines Vector-Objekts - Frage zum Typ Java Basics - Anfänger-Themen 3
S Objekt Erzeugung ohne new Operator Java Basics - Anfänger-Themen 6
M JTable nach erzeugung anpassen. Java Basics - Anfänger-Themen 4
C Erzeugung beliebiger Anzahl von Objekten in der ArrayList Java Basics - Anfänger-Themen 2
G Erzeugung eines Dreiecks und die contains Methode Java Basics - Anfänger-Themen 2
G Array-Länge bei Erzeugung noch unbekannt - wie erzeugen? Java Basics - Anfänger-Themen 12
T Fehler bei Datei-Erzeugung Java Basics - Anfänger-Themen 5
M Frage zu Object-Erzeugung Java Basics - Anfänger-Themen 2
M Länge eines Arrays als Variable speichern möglich? Java Basics - Anfänger-Themen 14
P Objekt einer Methode eines anderen Objektes übergeben Java Basics - Anfänger-Themen 5
P Wie kann ich beispielsweise Speicherstände eines Spiels DAUERHAFT in meinem Programm speichern? Java Basics - Anfänger-Themen 3
laxla123 Eigenschaften eines Algorithmus (determiniert vs.. deterministisch) Java Basics - Anfänger-Themen 2
monsterherz Ablauf der Erstellung eines Java Programmes Java Basics - Anfänger-Themen 17
monsterherz Fehler Semikolon fehlt - ich weiss aber nicht wo da noch eines hin sollte... Java Basics - Anfänger-Themen 21
J Farbe des Striches eines TitledBorders ändern Java Basics - Anfänger-Themen 2
pc pc pc pc pc letztes Element eines Arrays n Java Basics - Anfänger-Themen 3
walid Öffnungszeiten eines Geschäftes Java Basics - Anfänger-Themen 3
paulen1 Best Practice "Unchecked Assignment" Warnung beim erstellen eines 2D Arrays of Arraylists Java Basics - Anfänger-Themen 2
T Probleme beim Import eines Git-Repos Java Basics - Anfänger-Themen 2
U Eigenschaft eines JTextfiels per ActionListener ändern... Java Basics - Anfänger-Themen 2
B Synchronisation eines kleinen Museums Java Basics - Anfänger-Themen 47
krgewb Breite und Höhe eines Bildes in base64 auslesen Java Basics - Anfänger-Themen 3
Sachinbhatt Was ist die Notwendigkeit eines Sammlungsframeworks in Java? Java Basics - Anfänger-Themen 2
N Textdatei aus Resourcen-Ordner eines Projekts/ jar-file lesen Java Basics - Anfänger-Themen 4
B Produkt eines double - streams Java Basics - Anfänger-Themen 3
B Attribute eines Objekts einer Klasse durch statische Methode einer 2. Klasse ändern? Java Basics - Anfänger-Themen 32
S Variablen Letzte Zeile eines Strings entfernen Java Basics - Anfänger-Themen 1
D Inhalt eines Arrays ausgeben Java Basics - Anfänger-Themen 7
A Jedes zweite Element eines Arrays entfernen Java Basics - Anfänger-Themen 30
sserio Java Fx, wie erstellt man einen EventHandler, der durch das Drücken eines Button Texte in eine Table view einfügt Java Basics - Anfänger-Themen 17
J Größe eines Strings in Pixel Java Basics - Anfänger-Themen 18
M Parse-Tree eines statements darstellen Java Basics - Anfänger-Themen 0
H Java verkettete Liste, Wert eines Index zurückgeben Java Basics - Anfänger-Themen 1
bluetrix Programmieren eines Bots für Zahlen-Brettspiel Java Basics - Anfänger-Themen 9

Ähnliche Java Themen

Neue Themen


Oben