To make my question more clear, consider following use-case:
Suppose there is a package that permits a set of operations on a given platform, for example a class to edit the registry on Windows. This package does not exist on other platforms, as there is no equivalent operation on other operating system.
For the sake of simplicity, consider
windows/Registry.java
package windows; public class Registry { static Registry instance = null; static{ System.out.println("print from static block"); } private Registry() { System.out.println("Registry instance created!"); } public static synchronized Registry getInstance() { if (null == instance) { instance = new Registry(); } return instance; } public void foo() { System.out.println("foo called."); } }
and the class where I am going to conditionally use the registry: main/Main.java
package main; import windows.Registry; public class Main { public static void test1(boolean onWindows) { if (onWindows) { Registry instance = Registry.getInstance(); System.out.println("We are on Windows: "); instance.foo(); } else { System.out.println("We are somewhere else!"); } } public static void main(String[] args) { System.out.println("Entered main"); boolean onWindows = args.length > 0 ? Boolean.parseBoolean(args[0]) : false; test1(onWindows); } }
The question is, if no function or class is explicitly executed in Main.class
, is it guaranteed that no code from Registry.class
is executed?
I was able to test this example on multiple Desktop Platforms and with different java versions, but I would like to know if this behavior is documented and one can rely on it, if it thus the expected behavior also on other platforms, like android or embedded version of the JRE.
In case there is no such guarantee, as a (maybe custom) classloader might decide to load all .class
files in the classpath, can I assume that the code will work without a java.lang.NoClassDefFoundError
if onWindows
is false and I remove Registry.class
from the classpath?
The behavior I’ve observed so far is
rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && java -classpath bld main.Main true Entered main print from static block Registry instance created! We are on Windows: foo called. rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && java -classpath bld main.Main false Entered main We are somewhere else! rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && rm -r bld/windows && java -classpath bld main.Main false Entered main We are somewhere else! rm -rf bld; mkdir bld && javac -d bld src/windows/Registry.java src/main/Main.java && rm -r bld/windows && java -classpath bld main.Main true Entered main Exception in thread "main" java.lang.NoClassDefFoundError: windows/Registry at main.Main.test1(Main.java:9) at main.Main.main(Main.java:20) Caused by: java.lang.ClassNotFoundException: windows.Registry at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520) ... 2 more
Are those behavior (lazy classloader and removing Registry.class
) defined?
Advertisement
Answer
The question is, if no function or class is explicitly executed in
Main.class
, is it guaranteed that no code fromRegistry.class
is executed?
That doesn’t touch directly on class loading, but rather on class initialization, which is the first point at which any code from the class in question gets executed. Specifically, static initialization blocks and the initializers of static members are executed during this stage. The class in question must already have been loaded and verified at this point, but it might have been loaded an arbitrary amount of time earlier.
Per JLS 12.4.1,
A class or interface T will be initialized immediately before the first occurrence of any one of the following:
T is a class and an instance of T is created.
A
static
method declared by T is invoked.A
static
field declared by T is assigned.A
static
field declared by T is used and the field is not a constant variable
Thus, if you never instantiate the class or access access any of its static methods or fields (except to read a static field that is a “constant variable”) then no code from the class will ever be executed.
But that a class does not get initialized does not mean that no attempt will be made to load it. The JLS does not forbid implementations from loading classes prospectively. In fact, JLS 12.2.1 specifically says:
a class loader may cache binary representations of classes and interfaces, prefetch them based on expected usage, or load a group of related classes together.
Thus, no, it is not safe to rely on the application represented by your class main.Main
to run without a java.lang.NoClassDefFoundError
or other loading error when class windows.Registry
cannot be loaded, regardless of whether it can be expected actually to be used. However, you can, under the right circumstances, rely on that class not being initialized, and therefore no code from it being executed.