Class Loaders in Java

Naveen AnamaneniUpdated · 5 min read

A class loader is the part of the JVM that finds a class's bytecode and turns it into a Class object in memory. Classes are loaded lazily: the first time your code needs a class, not when the program starts. Java uses a chain of three built-in class loaders, and each loader first asks its parent before trying itself, which is called the parent delegation model. In this lesson, you will learn the built-in loaders, the delegation model, and when classes are loaded and initialized.

The Built-In Class Loaders

LoaderLoadsgetClassLoader() returns
BootstrapCore classes from java.base (java.lang.String, java.util.*)null (it's written in native code)
PlatformOther JDK modules (java.sql, java.net.http and so on)The platform class loader
Application (system)Your classes and libraries on the class path or module pathThe app class loader

The Parent Delegation Model

Ask the parent first; load it yourself only if the parents can't
  1. A loader receives a request for a class.
  2. It delegates to its parent first, all the way up to the bootstrap loader.
  3. Only if no parent can find the class does the loader search its own locations.

This guarantees that core classes such as java.lang.String always come from the JDK, so no library can replace them with its own version.

Example: Who Loaded What?

Let's see an example that prints the loader of several classes and the chain of parents.

Loaders.java▶ Try it
public class Loaders {
    public static void main(String[] args) throws Exception {
        System.out.println("String:  " + String.class.getClassLoader());
        System.out.println("Driver:  "
            + java.sql.Driver.class.getClassLoader().getName());
        System.out.println("Loaders: "
            + Loaders.class.getClassLoader().getName());
 
        ClassLoader loader = Loaders.class.getClassLoader();
        StringBuilder chain = new StringBuilder();
        while (loader != null) {
            chain.append(loader.getName()).append(" -> ");
            loader = loader.getParent();
        }
        System.out.println("Chain:   " + chain + "bootstrap");
    }
}
Output
String:  null
Driver:  platform
Loaders: null
Chain:   null -> app -> platform -> bootstrap

Explanation: String comes from the bootstrap loader, which Java represents as null. java.sql.Driver comes from the platform loader. Following getParent() gives the delegation chain. This example was run directly from source with java Loaders.java, so the launcher compiled it in memory and loaded it with its own unnamed class loader (its name is null), whose parent is the application loader app. Compiled with javac and run with java Loaders, the class is loaded by app itself, and the chain is app -> platform -> bootstrap.

Example: When Classes Are Loaded and Initialized

LazyLoading.java▶ Try it
public class LazyLoading {
    static class Config {
        static {
            System.out.println("Config initialized");
        }
        static String name() { return "Navika's config"; }
    }
 
    public static void main(String[] args) throws Exception {
        System.out.println("main started");
        Class<?> c = Class.forName("LazyLoading$Config", false,
                                   LazyLoading.class.getClassLoader());
        System.out.println("Loaded without initializing: " + c.getSimpleName());
        System.out.println(Config.name());
    }
}
Output
main started
Loaded without initializing: Config
Config initialized
Navika's config

Explanation: Class.forName(name, false, loader) loads the class but doesn't initialize it, so its static block doesn't run yet. The first real use, calling Config.name(), triggers initialization: the static block prints "Config initialized", then the method runs. Without that first use, the class would never be initialized at all.

The Steps of Class Loading

StepWhat happens
LoadingThe loader finds the bytecode and creates a Class object
Linking: verificationThe bytecode is checked for safety
Linking: preparationStatic fields get default values
Linking: resolutionSymbolic references to other classes are resolved (often lazily)
InitializationStatic initializers and static field assignments run, once, on first active use

A class is initialized on first active use: creating an instance, calling a static method, reading or writing a non-constant static field, or Class.forName(name) with the default arguments.

Common Mistakes

Confusing ClassNotFoundException with NoClassDefFoundError. The first means a class asked for by name (for example with Class.forName) wasn't found; the second means a class that was present at compile time is missing or failed to initialize at run time.

Assuming a class is loaded at startup. Most classes load only when first needed.

Expecting getClassLoader() to return an object for String. The bootstrap loader is represented as null.

Interview Corner

What is the parent delegation model in class loading?

Each class loader has a parent. When asked to load a class, a loader first delegates the request to its parent, recursively up to the bootstrap loader; only if the parents can't find the class does the loader try to find it itself. Java's built-in hierarchy is bootstrap (core java.base classes), platform (other JDK modules) and application (the class path and module path). Delegation ensures core classes are always loaded by the bootstrap loader and that each class is loaded once per loader hierarchy, which keeps the platform secure and consistent.

Practice

  1. Easy: Print the class loader of java.util.ArrayList, java.net.http.HttpClient and one of your own classes.
  2. Harder: Run a small program with -Xlog:class+load and count how many classes are loaded before your main method prints its first line.

Quick check: Class loaders

4 questions
1Which loader loads java.lang.String?
2In the delegation model, who does a loader ask first?
3When is a class initialized?
4Which option shows classes as they are loaded?

Key Takeaways

Was this tutorial helpful?

Lesson 447 of 482