Du verwendest einen veralteten Browser. Es ist möglich, dass diese oder andere Websites nicht korrekt angezeigt werden. Du solltest ein Upgrade durchführen oder ein alternativer Browser verwenden.
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();
}
}
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
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?
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.
Verwend einen [japi]URLClassloader[/japi], welchem du den Pfad zum Ordner mitgibst. Mit diesem Classloader kannst du dann z.B. [c]ExternalExternalClassTwo[/c] laden.
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?
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.
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:
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
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.
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.
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.
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.
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:
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].
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?
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).
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.
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:
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: