Fundamentals 17 min read

How Java Class Loaders Work: From Bootstrap to Custom Implementations

This tutorial explains Java class loaders—including bootstrap, platform, and system loaders—their delegation model, visibility rules, and how to create custom loaders, providing code examples and a clear overview of their role in the JVM.

Cognitive Technology Team
Cognitive Technology Team
Cognitive Technology Team
How Java Class Loaders Work: From Bootstrap to Custom Implementations

1. Introduction

Class Loader is an object responsible for loading classes dynamically into the JVM at runtime. They are part of the JRE, allowing the JVM to load classes without knowing the underlying file system.

The JVM loads classes on demand rather than loading all at once.

This tutorial discusses the different built‑in class loaders and their operation, then shows how to implement a custom loader.

2. What are the functions of a Class Loader?

Class loaders have two main functions:

Load classes – built‑in and custom loaders load classes. You can create a loader by extending java.lang.ClassLoader .

Locate resources – resources such as .class files, configuration files or images are packaged with the application or library for easy location.

Array classes are not created by the loader; the JVM creates them as needed. When you call Class#getClassLoader() on an array class, it returns the loader of its element type, or null for primitive types.

3. Types of Built‑in Class Loaders

Java supports three built‑in class loaders:

Bootstrap class loader – the VM’s built‑in loader, represented as null .

Platform class loader – loads platform classes, including the Java SE API and its implementations; it is the parent of the system class loader.

System (application) class loader – also called the application class loader; loads classes from the application class path, module path and JDK‑specific tools.

3.1 Example

First, see how different loaders load different classes:

public void printClassLoaders() throws ClassNotFoundException {
    System.out.println("Platform Class Loader:" + ClassLoader.getPlatformClassLoader());
    System.out.println("System Class Loader:" + ClassLoader.getSystemClassLoader());
    System.out.println("This class's loader:" + PrintClassLoader.class.getClassLoader());
    System.out.println("DriverManager's loader:" + DriverManager.class.getClassLoader());
    System.out.println("ArrayList's loader:" + ArrayList.class.getClassLoader());
}

Running the method prints:

Platform Class Loader:jdk.internal.loader.ClassLoaders$PlatformClassLoader@5674cd4d
System Class Loader:jdk.internal.loader.ClassLoaders$AppClassLoader@33909752
This class's loader:jdk.internal.loader.ClassLoaders$AppClassLoader@33909752
DriverManager's loader:jdk.internal.loader.ClassLoaders$PlatformClassLoader@5674cd4d
ArrayList's loader:null

The three loaders are bootstrap (shown as null ), platform, and system. The system loader loads the class containing the example method. The platform loader loads DriverManager . The bootstrap loader loads ArrayList . The bootstrap loader is native code, so it appears as null.

3.2 Bootstrap Class Loader

The bootstrap loader loads core JDK classes from $JAVA_HOME/jre/lib/rt.jar and other core libraries. It is the parent of all other loaders and is implemented in native code.

3.3 Platform Class Loader

The platform loader is a child of the bootstrap loader and loads standard core Java classes, making them available to all applications on the platform.

3.4 System Class Loader

The system (application) loader loads classes found on the class‑path or via -classpath / -cp options. It is a child of the platform loader.

4. How Class Loaders Work

When the JVM requests a class, java.lang.ClassLoader.loadClass(String name, boolean resolve) attempts to load it. The method first calls findLoadedClass , then delegates to the parent loader via loadClass(String) , and finally invokes findClass if needed. If resolve is true, resolveClass is called on the resulting Class object. If the class cannot be found, a ClassNotFoundException is thrown.

4.1 Delegation Model

The delegation model means a loader delegates the search for a class or resource to its parent before attempting to load it itself. This hierarchical approach ensures uniqueness of classes.

4.2 Unique Classes

Because of delegation, a class is loaded only once in the hierarchy, guaranteeing uniqueness.

4.3 Visibility

Child loaders can see classes loaded by their parents, but not vice‑versa. For example, classes loaded by the system loader can see those loaded by the platform and bootstrap loaders, while the platform loader cannot see classes loaded only by the system loader.

5. Custom ClassLoader

Built‑in loaders are sufficient for most file‑system classes, but custom loaders are useful when loading classes from non‑standard locations such as network resources.

Use cases include byte‑code manipulation, dynamic class creation (e.g., JDBC driver switching), and versioned loading of classes with the same name.

Example of a custom loader that reads a class file into a byte array and defines the class:

public class CustomClassLoader extends ClassLoader {
    @Override
    public Class findClass(String name) throws ClassNotFoundException {
        byte[] b = loadClassFromFile(name);
        return defineClass(name, b, 0, b.length);
    }

    private byte[] loadClassFromFile(String fileName) {
        InputStream inputStream = getClass().getClassLoader().getResourceAsStream(
                fileName.replace('.', File.separatorChar) + ".class");
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        int nextValue;
        try {
            while ((nextValue = inputStream.read()) != -1) {
                byteStream.write(nextValue);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return byteStream.toByteArray();
    }
}

This loader extends the default loader and loads class bytes from a specified file.

6. java.lang.ClassLoader API Overview

6.1 loadClass()

Loads a class by its fully‑qualified name. The JVM calls this method with resolve set to true, but you can set it to false to simply check for existence.

6.2 defineClass()

Converts a byte array into a Class instance; throws ClassFormatError if the data is invalid. This method is final and cannot be overridden.

6.3 findClass()

Finds a class by name; custom loaders override this after delegation fails. The default implementation throws ClassNotFoundException .

6.4 getParent()

Returns the parent loader used for delegation; the bootstrap loader is represented by null.

6.5 getResource()

Attempts to locate a resource by name, delegating to the parent first, then searching the VM’s built‑in paths. It returns a URL or null if the resource cannot be found or access is denied.

7. Context Class Loaders

Context class loaders provide an alternative to the standard delegation model, allowing core JDK code to load classes supplied by applications. Threads expose getContextClassLoader() , which returns the loader set by the thread creator. Since Java 9, threads in the fork/join common pool use the system class loader as their context loader.

8. Conclusion

Class loaders are essential for executing Java programs. This article introduced the bootstrap, platform, and system loaders, explained their delegation, visibility, and uniqueness properties, showed how to create a custom loader, and covered context class loaders.

JVMClass Loadercustom-loaderdelegation-model
Cognitive Technology Team
Written by

Cognitive Technology Team

Cognitive Technology Team regularly delivers the latest IT news, original content, programming tutorials and experience sharing, with daily perks awaiting you.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.