Java, die plattformunabhängige Programmiersprache von Sun

Dynamisches Laden einer Klasse

Inhalt dieses Beispiels

Im vorliegenden Beispiel sollen einige Möglichkeiten dargestellt werden, wie zur Laufzeit geprüft werden kann, ob eine Klasse verfügbar ist und ob sie instanziierbar ist.

Einleitung

In Javaapplikationen, insbesondere auch in vernetzten Anwendungen, werden konkrete Javaklassen meist nur bei Bedarf geladen. Hierdurch verringert sich einerseits die Ladezeit der Anwendung, andererseits wird hierdurch aber auch der Arbeitsspeicher geringer beansprucht und die Netzbelastung wird minimiert. Allerdings besteht die Möglichkeit, dass es unter Umständen zu Fehlermeldungen kommt, wenn eine zu ladende Klasse nicht aufgefunden werden kann.

Um den aus diesen Fehlermeldungen resultierenden Frust beim Anwender abzuwenden, sollte garantiert werden, dass solche Situationen nutzerfreundlich abgefangen werden.

Ein Beispiel, wie es nicht ablaufen sollte.

import de.javaprog.ApiClass;	// Klasse aus dem Paket de.javaprog

public class KlassenAufruf{
	/* Instanzvariable zur Speicherung einer Anwendungsklasse */
	Object apiClass;

	/** Initialisierung aller Variablen, usw. **/
	public void init(){
		apiClass = new ApiClass();
		/* ... */
	}
	/* ... */
	public static void main(String[] args){
		KlassenAufruf c = new KlassenAufruf();
		c.init();
		/* ... */
	}
}
Auszug aus dem Quellcode der Klasse "KlassenAufruf"

Der Compiler mag diese Anweisungsfolge durchaus noch fehlerfrei in entsprechenden Bytecode übersetzen. Aber was passiert, wenn im Aufrufzeitpunkt (also zB. beim Anwender) das Javapaket de.javaprog nicht zugreifbar ist?

Exception in thread "main" java.lang.NoClassDefFoundError: de/javaprog/ApiClass
        at KlassenAufruf.init(KlassenAufruf.java:9)
        at KlassenAufruf.main(KlassenAufruf.java:15)
Beispielmeldung eines Laufzeitfehlers

Wie soetwas passieren kann?

Abfangen von Laufzeitfehlern

Wie lässt sich der oben dargestellte Laufzeitfehler abfangen?
Eine der möglichen Methoden des Abfangens von Laufzeitfehlern besteht in der Nutzung von try-catch-Blöcken.

import de.javaprog.ApiClass;	// Klasse aus dem Paket de.javaprog

public class KlassenAufruf{
	/* Instanzvariable zur Speicherung einer Anwendungsklasse */
	Object apiClass;

	/** Initialisierung aller Variablen, usw. **/
	public void init(){
		try{
			apiClass = new ApiClass();
		}catch(NoClassDefFoundError e){
			/* Behandlung des Fehlers zum Wohle des Anwenders, zB. */
			System.out.println("Die Klasse: "+e.getMessage()+" ist abhanden gekommen!");
			System.exit(-4);
		}
		/* ... */
	}
	/* ... */
	public static void main(String[] args){
		KlassenAufruf c = new KlassenAufruf();
		c.init();
		/* ... */
	}
}
Auszug aus dem erweiterten Quellcode der Klasse "KlassenAufruf"

Sollte nun die Klasse de.javaprog.ApiClass zur Laufzeit nicht verfügbar sein, erhalten wir:

Die Klasse: de/javaprog/ApiClass konnte nicht gefunden werden
Angepasste Beispielmeldung eines Laufzeitfehlers

Nutzung der JUnit-Tests

Es wäre reine Verschwendung von Programmzeilen, würde jede Anweisung zur Instanziierung einer Javaklasse jedesmal erst daraufhin untersucht werden, ob diese Klasse auch tatsächlich in der aktuellen Laufzeitversion zur Verfügung steht. Für den (extremen) Programmierer ist es ohnehin Ehrensache, zu jeder Klasse eine entsprechende Testklasse geschrieben zu haben.

Möglich ist es daher, in der JUnit-Testklasse eine Methode zur dynamischen Überprüfung des Vorhandenseins der zu testenden Klasse zu definieren.

package de.javaprog.tests;

import junit.framework.*;

/** JUnit-Testklasse zur Klasse ApiClass **/
public class TestApiClass extends TestCase{
	/** Konstruktor der Testklasse **/
	public TestApiClass(String name){
		super(name);
	}
	/** Test der Verfügbarkeit von ApiClass **/
	public void testExistence(){
		Class testObject = null;
		try{
			testObject = Class.forName("de.javaprog.ApiClass");
		}catch(ClassNotFoundException e){
			fail("Die Klasse ApiClass muss im Paket de.javaprog existieren und erreichbar sein!");
		}
	}
	/* ... */
}
Auszug aus der JUnit-Testklasse zur Klasse "ApiClass"

Nebenbei bemerkt: die Verwendung der Methode Class.forName(...) ermöglicht das fehlerfreie Kompilieren der Testklasse, ohne dass die zu testende Klasse (also: de.javaprog.ApiClass) bereits vorhanden sein muss.
Sollte nun also die Klasse de.javaprog.ApiClass zur Laufzeit nicht verfügbar sein, erhalten wir:

testExistence(de.javaprog.tests.TestApiClass):Die Klasse ApiClass muss im Paket de.javaprog existieren und erreichbar sein!
Angepasste Beispielmeldung eines Laufzeitfehlers in der JUnit-Testklasse

Zusätzlich kann auch die Instanziierbarkeit der zu testenden Klasse überprüft werden.

	/* ... /*
	/** Test der Instanziierbarkeit von ApiClass **/
	public void testInstantiation(){
		Class testObject=null;
		try{
			testObject = Class.forName("de.javaprog.ApiClass");
		}catch(ClassNotFoundException e){
			fail("Die Klasse ApiClass muss im Paket de.javaprog existieren und erreichbar sein!");
		}
		try{
			Object testInstance = testObject.newInstance();
		}catch(InstantiationException e){
			fail("Instanz von ApiClass konnte nicht erzeugt werden! (ist die Klasse 'abstract'?)");
		}catch(IllegalAccessException e){
			fail("Die Klasse bzw. ihr leerer Konstruktor konnte nicht instanziiert werden!");
		}
	}
	/* ... */
}
Auszug aus der JUnit-Testklasse zur Klasse "ApiClass"

© copyright by Horst Liske, Softwareprojektierung & EDV-Beratung. Nachnutzung mit Quellenangabe ausdrücklich erwünscht.

Hinweise und Fragen zu diesen Beipielen bitte an webmaster@javaprog.de. zur Übersicht