Überprüfen, ob eine Klasse in Java vorhanden ist

1. Übersicht

Das Überprüfen auf das Vorhandensein einer Klasse kann hilfreich sein, um festzustellen, welche Implementierung einer Schnittstelle verwendet werden soll. Diese Technik wird häufig bei älteren JDBC-Setups verwendet.

In diesem Tutorial werden die Nuancen der Verwendung von Class.forName () untersucht , um die Existenz einer Klasse im Java-Klassenpfad zu überprüfen .

2. Verwenden von Class.forName ()

Wir können die Existenz einer Klasse mithilfe von Java Reflection überprüfen, insbesondere Class.forName () . Die Dokumentation zeigt, dass eine ClassNotFoundException ausgelöst wird, wenn die Klasse nicht gefunden werden kann.

2.1. Wann ist mit ClassNotFoundException zu rechnen?

Schreiben wir zunächst einen Test, der mit Sicherheit eine ClassNotFoundException auslöst, damit wir wissen, dass unsere positiven Tests sicher sind:

@Test(expected = ClassNotFoundException.class) public void givenNonExistingClass_whenUsingForName_thenClassNotFound() throws ClassNotFoundException { Class.forName("class.that.does.not.exist"); }

Wir haben also bewiesen, dass eine Klasse, die nicht existiert, eine ClassNotFoundException auslöst . Schreiben wir einen Test für eine Klasse, die tatsächlich existiert:

@Test public void givenExistingClass_whenUsingForName_thenNoException() throws ClassNotFoundException { Class.forName("java.lang.String"); }

Diese Tests beweisen, dass das Ausführen von Class.forName () und das Nichtabfangen einer ClassNotFoundException der angegebenen Klasse entspricht, die im Klassenpfad vorhanden ist . Dies ist jedoch aufgrund von Nebenwirkungen keine perfekte Lösung.

2.2. Nebeneffekt: Klasseninitialisierung

Es ist wichtig darauf hinzuweisen, dass Class.forName () ohne Angabe eines Klassenladeprogramms den statischen Initialisierer für die angeforderte Klasse ausführen muss . Dies kann zu unerwartetem Verhalten führen.

Um dieses Verhalten zu veranschaulichen, erstellen wir eine Klasse, die eine RuntimeException auslöst, wenn ihr statischer Initialisierungsblock ausgeführt wird, damit wir sofort wissen, wann er ausgeführt wird:

public static class InitializingClass { static { if (true) { //enable throwing of an exception in a static initialization block throw new RuntimeException(); } } }

Aus der forName () -Dokumentation geht hervor, dass ein ExceptionInInitializerError ausgelöst wird , wenn die durch diese Methode ausgelöste Initialisierung fehlschlägt.

Schreiben wir einen Test, der einen ExceptionInInitializerError erwartet, wenn versucht wird, unsere InitializingClass zu finden, ohne einen Klassenlader anzugeben:

@Test(expected = ExceptionInInitializerError.class) public void givenInitializingClass_whenUsingForName_thenInitializationError() throws ClassNotFoundException { Class.forName("path.to.InitializingClass"); }

Da die Ausführung des statischen Initialisierungsblocks einer Klasse ein unsichtbarer Nebeneffekt ist, können wir jetzt sehen, wie dies zu Leistungsproblemen oder sogar Fehlern führen kann. Schauen wir uns an, wie die Klasseninitialisierung übersprungen wird.

3. Weisen Sie Class.forName () an, die Initialisierung zu überspringen

Zum Glück gibt es eine überladene Methode von forName (), die einen Klassenlader akzeptiert und angibt, ob die Klasseninitialisierung ausgeführt werden soll.

Laut Dokumentation sind folgende Aufrufe gleichwertig:

Class.forName("Foo") Class.forName("Foo", true, this.getClass().getClassLoader())

Durch Ändern von true in false können wir jetzt einen Test schreiben, der die Existenz unserer InitializingClass überprüft, ohne den statischen Initialisierungsblock auszulösen :

@Test public void givenInitializingClass_whenUsingForNameWithoutInitialization_thenNoException() throws ClassNotFoundException { Class.forName("path.to.InitializingClass", false, getClass().getClassLoader()); }

4. Java 9-Module

Für Java 9+ Projekte, gibt es eine dritte Überlastung von Class.forName () , die einen übernimmt Modul und einen String - Klassennamen. Diese Überladung führt den Klasseninitialisierer standardmäßig nicht aus. Auch, vor allem, es gibt null , wenn die angeforderte Klasse nicht existiert , anstatt einen Wurf ClassNotFoundException .

5. Schlussfolgerung

In diesem kurzen Tutorial haben wir den Nebeneffekt der Klasseninitialisierung bei der Verwendung von Class.forName () aufgezeigt und festgestellt, dass Sie die Überladungen forName () verwenden können , um dies zu verhindern.

Der Quellcode mit allen Beispielen in diesem Tutorial finden Sie auf GitHub.