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.
All source code for this tutorial is uploaded on GitHub here.
If you are using Eclipse:
Run the command
mvn eclipse:eclipseto generate Eclipse’s project files.
- Load the generated project.
Set output path to
If you are using IntelliJ:
Import the project’s
- 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
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
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
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.
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
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.