Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java class loaders: type identity, delegation and initialization

Last updated: 29 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

A Java type is identified by its binary name and defining class loader; loading a class and initializing it are separate runtime steps.

Java 8+. This is a complete program using JDK classes.

Locate a type before creating an instance

A plugin loader can discover a class name before deciding to run its initialization code. Class.forName with initialize=false supports that inspection step. Initializing the class later can execute static initializers, including code that allocates resources or fails.

The program prints when its nested configuration class initializes. Looking it up without initialization produces no configuration message; the second request triggers that work once. Keep this difference in mind when scanning classes in a build tool or application startup routine.

A matching binary name alone does not make types from separate defining loaders interchangeable. A plugin should share a service interface through a common parent loader. Otherwise casting a plugin instance to the host interface can fail even when both interfaces have the same printed name.

Do not retain a loader accidentally

Parent delegation is a common loading strategy, not an excuse to assume every custom loader makes the same choices. A loader can also remain reachable through static registries, a thread context loader or long-lived callbacks. Unloading depends on reachability; closing a URLClassLoader closes its resources without forcing the VM to unload all of its types.

Working program

Java
public class ConfigurationClassLoading {
    static class Configuration {
        static { System.out.println("Configuration initialized"); }
    }
    public static void main(String[] args) throws Exception {
        ClassLoader loader = ConfigurationClassLoading.class.getClassLoader();
        String name = "ConfigurationClassLoading$Configuration";
        Class<?> inspected = Class.forName(name, false, loader);
        System.out.println("Inspected without initialization");
        Class<?> initialized = Class.forName(name, true, loader);
        System.out.println(inspected == initialized);
        System.out.println(String.class.getClassLoader() == null);
    }
}

Output

Output
Inspected without initialization
Configuration initialized
true
true

Costs and boundaries

Repeated lookup costs depend on loader work and whether the class has already been defined. Initialization can execute arbitrary application code, so it has no universal constant-time budget. Keep scanning separate from resource acquisition and record initializer failures at the startup boundary.

Common Mistakes

  • The same class name can identify different runtime types.
  • Loading without initialization does not guarantee that every subsequent reflective action avoids initialization.
  • Closing a loader does not force class unloading.

Read next

Initialization triggers, Reflective access.

java
class-loaders
Storage details