Understanding Java Class Loaders and Delegation

1. Class Uniqueness and Class Loaders

In the Java Virtual Machine (JVM), the uniqueness of a class is determined by its fully qualified name plus the class loader that loaded it.

<class loader instance> + <package.class name>

Two classes with the same name loaded by different class loaders are treated as completely distinct types by the JVM.

2. Parent Delegation Model

Java’s class loading mechanism follows the Parent Delegation Model, where a class loader first delegates the class-loading request to its parent before attempting to load the class itself.

2.1 Types of Class Loaders

| Class Loader | Implemented In | Responsibility | |————–|—————-|—————-| | Bootstrap ClassLoader | C++ | Loads core Java classes from JAVA_HOME/lib and classes specified by -Xbootclasspath. Appears as null in output. | | Extension ClassLoader | Java | Loads classes from JAVA_HOME/lib/ext or paths specified by java.ext.dirs. | | Application ClassLoader | Java | Loads classes from the user classpath (classpath). This is the default loader for most applications. |

As of Java 9, the Extension ClassLoader has been removed and replaced by the Platform ClassLoader, reflecting the introduction of the Java Module System.

3. Class Loader Hierarchy (Java 9+)

From Java 9 onward, the Platform ClassLoader replaces the Extension ClassLoader and is responsible for loading platform modules (excluding core modules).

                +--------------------------+
                |   Bootstrap ClassLoader  |
                +--------------------------+
                            |
                +--------------------------+
                |Platform ClassLoader (new)|
                +--------------------------+
                            |
                +--------------------------+
                |  Application ClassLoader |
                +--------------------------+

4. Scenarios that Break the Parent Delegation Model

Although delegation is the default model, certain real-world use cases break this pattern deliberately.

4.1 JDBC Mechanism

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.18</version>
</dependency>
public class DriverStudy {
    public static void main(String[] args) throws SQLException {
        System.out.println(Driver.class.getClassLoader());
        System.out.println(DriverManager.class.getClassLoader());
        System.out.println(com.mysql.cj.jdbc.Driver.class.getClassLoader());

        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306", "root", "123456");
    }
}

Sample output (Java 8):

null  // Driver and DriverManager are loaded by Bootstrap ClassLoader
null
sun.misc.Launcher$AppClassLoader@xxx  // MySQL driver loaded by Application ClassLoader

4.2 Internal Analysis of JDBC Loading

Breaking Point

Although DriverManager is loaded by the Bootstrap ClassLoader, it uses the thread context class loader—typically the Application ClassLoader—to discover and load third-party JDBC drivers dynamically.

This breaks the parent delegation model and follows the Service Provider Interface (SPI) pattern, which relies on the META-INF/services directory and the ServiceLoader mechanism.

5. Java 9+ Module System and Class Loading

With the introduction of Java Modules (Jigsaw) in Java 9:

The Java 9+ module system formally relaxes strict parent delegation, favoring modular encapsulation.

6. Other Scenarios that Break Delegation

Frameworks such as JNDI and OSGi often intentionally break the parent delegation model by using custom class loader hierarchies. These hierarchies are designed to support modularization, dynamic loading, and hot-swapping of components.

Such frameworks typically rely on:

This approach enables better isolation, versioning, and runtime flexibility, which are essential in modular systems or plugin-based architectures.