Partner – Microsoft – NPI (cat=Java)
announcement - icon

Microsoft JDConf 2024 conference is getting closer, on March 27th and 28th. Simply put, it's a free virtual event to learn about the newest developments in Java, Cloud, and AI.

Josh Long and Mark Heckler are kicking things off in the keynote, so it's definitely going to be both highly useful and quite practical.

This year’s theme is focused on developer productivity and how these technologies transform how we work, build, integrate, and modernize applications.

For the full conference agenda and speaker lineup, you can explore JDConf.com:

>> RSVP Now

1. Overview

Checking for the existence of a class could be useful when determining which implementation of an interface to use. This technique is commonly used during older JDBC setups.

In this tutorial, we’ll explore the nuances of using Class.forName() to check the existence of a class in the Java classpath.

2. Using Class.forName()

We can check for the existence of a class using Java Reflection, specifically Class.forName(). The documentation shows that a ClassNotFoundException will be thrown if the class cannot be located.

2.1. When to Expect ClassNotFoundException

First, let’s write a test that will certainly throw a ClassNotFoundException so that we can know that our positive tests are safe:

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

So, we’ve proven that a class that doesn’t exist will throw a ClassNotFoundException. Let’s write a test for a class that indeed does exist:

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

These tests prove that running Class.forName() and not catching a ClassNotFoundException is equivalent to the specified class existing on the classpath. However, this isn’t quite a perfect solution due to side effects.

2.2. Side Effect: Class Initialization

It is essential to point out that, without specifying a class loader, Class.forName() has to run the static initializer on the requested class. This can lead to unexpected behavior.

To exemplify this behavior, let’s create a class that throws a RuntimeException when its static initializer block is executed so that we can know immediately when it is executed:

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

We can see from the forName() documentation that it throws an ExceptionInInitializerError if the initialization provoked by this method fails.

Let’s write a test that will expect an ExceptionInInitializerError when trying to find our InitializingClass without specifying a class loader:

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

Since the execution of a class’ static initialization block is an invisible side effect, we can now see how it could cause performance issues or even errors. Let’s look at how to skip the class initialization.

3. Telling Class.forName() to Skip Initialization

Luckily for us, there is an overloaded method of forName(), which accepts a class loader and whether the class initialization should be executed.

According to the documentation, the following calls are equivalent:

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

By changing true to false, we can now write a test that checks for the existence of our InitializingClass without triggering its static initialization block:

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

4. Java 9 Modules

For Java 9+ projects, there’s a third overload of Class.forName(), which accepts a Module and a String class name. This overload doesn’t run the class initializer by default. Also, notably, it returns null when the requested class does not exist rather than throwing a ClassNotFoundException.

5. Conclusion

In this short tutorial, we’ve exposed the side effect of class initialization when using Class.forName() and have found that you can use the forName() overloads to prevent that from happening.

The source code with all the examples in this tutorial can be found over on GitHub.

Course – LS (cat=Java)

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
2 Comments
Oldest
Newest
Inline Feedbacks
View all comments
Comments are closed on this article!