Advanced Java Class Tutorial: A Guide to Class Reloading

In Java development, a typical workflow involves restarting the server with every class change, and no one complains about it. That is a fact about Java development. We have worked like that since our first day with Java. But is Java class reloading that difficult to achieve? And could that problem be both challenging and exciting to solve for skilled Java developers? In this Java class tutorial, I will try to address the problem, help you gain all the benefits of on-the-fly class reloading, and boost your productivity immensely.

Java class reloading is not often discussed, and there is very little documentation exploring this process. I’m here to change that. This Java classes tutorial will provide a step by step explanation of this process and help you master this incredible technique. Keep in mind that implementing Java class reloading requires a great deal of care, but learning how to do it will put you in the big leagues, both as a Java developer, and as a software architect. It also will not hurt to understand how to avoid the 10 most common Java mistakes.

Work-Space Setup

All source code for this tutorial is uploaded on GitHub here.

To run the code while you follow this tutorial, you will need Maven, Git and either Eclipse or IntelliJ IDEA.

If you are using Eclipse:

  • Run the command mvn eclipse:eclipse to generate Eclipse’s project files.
  • Load the generated project.
  • Set output path to target/classes.

If you are using IntelliJ:

  • Import the project’s pom file.
  • IntelliJ will not auto-compile when you are running any example, so you have to either
  • Run the examples inside IntelliJ, then every time you want to compile, you’ll have to press Alt+B E
  • Run the examples outside IntelliJ with the run_example*.bat. Set IntelliJ’s compiler auto-compile to true. Then, every time you change any java file, IntelliJ will auto-compile it.

Example 1: Reloading a Class with Java Class Loader

The first example will give you a general understanding of the Java class loader. Here is the source code.

Given the following User class definition:

We can do the following:

In this tutorial example, there will be two User classes loaded into the memory. userClass1 will be loaded by the JVM’s default class loader, and userClass2 using the DynamicClassLoader, a custom class loader whose source code is also provided in the GitHub project, and which I will describe in detail below.

Here is the rest of the main method:

And the output:

As you can see here, although the User classes have the same name, they are actually two different classes, and they can be managed, and manipulated, independently. The age value, although declared as static, exists

in two versions, attaching separately to each class, and can be changed independently as well.

In a normal Java program, ClassLoader is the portal bringing classes into the JVM. When one class requires another class to be loaded, it’s the ClassLoader’s task to do the loading.

However, in this Java class example, the custom ClassLoader named DynamicClassLoader is used to load the second version of User class. If instead of DynamicClassLoader, we were to use the default class loader again ( with the command StaticInt.class.getClassLoader() ) then the same User class will be used, as all loaded classes are cached.

Examining the way the default Java ClassLoader works versus DynamicClassLoader is key to benefiting from this Java classes tutorial.

The DynamicClassLoader

There can be multiple classloaders in a normal Java program. The one that loads your main class, ClassLoader, is the default one, and from your code, you can create and use as many classloaders as you like. This, then, is the key to class reloading in Java. The DynamicClassLoader is possibly the most important part of this entire tutorial, so we must understand how dynamic class loading works before we can accomplish our goal.

Unlike the default behavior of ClassLoader, our DynamicClassLoader inherits a more aggressive strategy. A normal classloader would give its parent ClassLoader the priority and only load classes that its parent can not load. That is suitable for normal circumstances, but not in our case. Instead, the DynamicClassLoader will try to look through all its class paths and resolve the target class before it gives up the right to its parent.

In our example above, the DynamicClassLoader is created with only one class path: "target/classes" (in our current directory), so it’s capable of loading all the classes that reside in that location. For all the classes not in there, it will have to refer to the parent classloader. For example, we need to load the String class in our StaticInt class, and our class loader does not have access to the rt.jar in our JRE folder, so the Stringclass of the parent class loader will be used.

The following code is from AggressiveClassLoader, the parent class of DynamicClassLoader, and shows where this behavior is defined.