Проверка дали съществува клас в Java

1. Общ преглед

Проверката за съществуването на клас може да бъде полезна при определяне коя реализация на интерфейс да се използва. Тази техника често се използва по време на по-стари настройки на JDBC.

В този урок ще изследваме нюансите на използването на Class.forName (), за да проверим съществуването на клас в Java classpath .

2. Използване на Class.forName ()

Можем да проверим за съществуването на клас, използвайки Java Reflection, по-специално Class.forName () . Документацията показва, че ClassNotFoundException ще бъде хвърлен, ако класът не може да бъде локализиран.

2.1. Кога да очаквате ClassNotFoundException

Първо, нека напишем тест, който със сигурност ще хвърли ClassNotFoundException, за да можем да знаем, че нашите положителни тестове са безопасни:

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

И така, доказахме, че клас, който не съществува, ще хвърли ClassNotFoundException . Нека напишем тест за клас, който наистина съществува:

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

Тези тестове доказват, че стартирането на Class.forName () и не улавянето на ClassNotFoundException е еквивалентно на посочения клас, съществуващ в пътя на класа . Това обаче не е съвсем идеално решение поради странични ефекти.

2.2. Страничен ефект: Инициализация на класа

Важно е да се отбележи, че без да се посочва зареждащ клас, Class.forName () трябва да стартира статичния инициализатор на заявения клас . Това може да доведе до неочаквано поведение.

За да илюстрираме това поведение, нека създадем клас, който хвърля RuntimeException, когато се изпълнява неговият статичен инициализационен блок, за да можем веднага да знаем кога е изпълнен:

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

От документацията за forName () можем да видим, че тя хвърля ExceptionInInitializerError, ако инициализацията, провокирана от този метод, се провали.

Нека напишем тест, който ще очаква ExceptionInInitializerError, когато се опитва да намери нашия InitializingClass, без да посочва зареждащ клас:

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

Тъй като изпълнението на статичен блок за инициализация на клас е невидим страничен ефект, сега можем да видим как може да причини проблеми с производителността или дори грешки. Нека да разгледаме как да пропуснем инициализацията на класа.

3. Кажете на Class.forName (), за да пропуснете инициализацията

За наш късмет има претоварен метод на forName (), който приема зареждащ клас и дали инициализацията на класа трябва да бъде изпълнена.

Според документацията следните обаждания са еквивалентни:

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

Чрез промяна на true на false , вече можем да напишем тест, който проверява за съществуването на нашия InitializingClass, без да задейства неговия статичен блок за инициализация :

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

4. Модули Java 9

За проектите Java 9+ има трето претоварване на Class.forName () , което приема име на модул и String клас. Това претоварване не изпълнява инициализатора на клас по подразбиране. Също така, по-специално, той връща null, когато исканият клас не съществува, вместо да хвърля ClassNotFoundException .

5. Заключение

В този кратък урок разкрихме страничния ефект от инициализацията на класа при използване на Class.forName () и установихме, че можете да използвате forName () претоварвания, за да предотвратите това.

Изходният код с всички примери в този урок може да бъде намерен в GitHub.