Hello! In our last lesson, we explored how the JVM handles initialization by compiling your code into special <init> and <clinit> methods. We saw that the <clinit> method for class-level static initialization is invoked automatically by the JVM. Today, we'll shift our focus to the mechanism that makes this possible: the class loader subsystem.
This lesson will introduce you to the agents responsible for finding and loading your .class files into the JVM. You will learn about the strict hierarchy of built-in class loaders and the elegant parent-delegation model they follow. Understanding this architecture is key to grasping how Java ensures type safety and security within its runtime environment.
The Class Loader Hierarchy
At its core, a class loader is an object responsible for dynamically loading classes into the JVM on demand. Instead of a single, monolithic loader, Java employs a hierarchical system of three primary class loaders, each with a distinct responsibility.
To get a clear definition of these three loaders, let's turn to a concise overview. Note that since Java 9, the "Extension Class Loader" has been renamed to "Platform Class Loader," though you will still see the older term used frequently.
This section from GeeksforGeeks provides a direct summary of the three main class loaders and their roles.
Read the section titled "Types of ClassLoaders", which covers the Bootstrap, Platform, and System (Application) loaders. Pay attention to what each loader is responsible for loading and its position in the hierarchy.
To summarize what you've just read:
-
Bootstrap Class Loader: The "primordial" loader at the top of the hierarchy. It's part of the core JVM, written in native code, and is responsible for loading the most fundamental Java classes like
java.lang.Object,java.lang.String, and other core runtime libraries from the JDK. Because it's native, it often appears asnullwhen you try to get a reference to it in Java code. -
Platform Class Loader: The child of the Bootstrap loader. It loads platform-specific classes, which include various standard libraries that are bundled with the JRE but are not part of the core Java SE specification.
-
Application (or System) Class Loader: The child of the Platform loader. This is the loader that you'll interact with most often. It is responsible for loading the classes of your own application from the classpath. When you run
java -cp . MyClass, it's the Application Class Loader that finds and loadsMyClass.class.
This parent-child relationship forms a strict hierarchy: Application -> Platform -> Bootstrap. This structure is fundamental to how class loading works in Java, a process governed by the parent-delegation model.
The Parent-Delegation Model
The parent-delegation model is a simple but powerful algorithm that class loaders follow to find and load classes. It ensures that classes are loaded in a predictable and secure manner. The entire process is beautifully illustrated in the following diagram.

The flow works as follows:
- A request to load a class (e.g.,
com.myapp.MyClass) is given to a class loader, typically the Application Class Loader. - Before attempting to load the class itself, the class loader first delegates the request to its parent.
- The parent (Platform Class Loader) does the same: it delegates the request to its parent (Bootstrap Class Loader).
- The request arrives at the Bootstrap Class Loader. It checks if it can find and load the class from its designated location (the core Java libraries).
- If yes, it loads the class, and the resulting
Classobject is returned down the chain. The process stops here. - If no, it informs its child (the Platform Class Loader) that it failed.
- If yes, it loads the class, and the resulting
- The Platform Class Loader now attempts to load the class from its own locations (e.g., JDK extension directories).
- If yes, it loads the class, and the result is returned.
- If no, it informs its child (the Application Class Loader) that it also failed.
- Finally, the Application Class Loader attempts to load the class from its path (the application classpath).
- If yes, it loads the class.
- If no, it throws a
ClassNotFoundException.
The key principle is "delegate first, load last." A class loader only tries to load a class itself after all its parents have failed.
For a more detailed textual walkthrough of this model, the Baeldung article provides an excellent explanation.
This section delves into the mechanics of the delegation model.
Please read section 4, starting with "How Class Loaders Work", and focusing on subsection 4.1, "Delegation Model." This will reinforce the steps described above.
Alternatively, if you prefer a narrated explanation, this video segment walks through the same delegation flow.
#1 Mastering Java Class Loaders: Dynamic Class Loading and Custom Class Loaders | Part-1
This video from TheCodeAlchemist provides a clear, animated explanation of the class loader hierarchy and the delegation model.
Watch the segment from the hierarchy explanation to see a step-by-step walkthrough of a class loading request propagating up and down the chain.
Consequences of the Delegation Model
This "delegate-up" design isn't just an arbitrary choice; it provides two critical guarantees for the stability and security of the Java platform.
-
Uniqueness of Classes: Because the search always starts from the top, a class is loaded by the first loader in the hierarchy that finds it. This ensures that a class with a specific fully qualified name (e.g.,
java.util.ArrayList) is loaded exactly once. This prevents a bizarre situation where you could have two differentArrayListclass objects in memory, which would break type-checking and lead to chaos. -
Security and Visibility: The delegation model acts as a security sandbox. Child loaders can see classes loaded by their parents, but parents cannot see classes loaded by their children. This is called the visibility principle. For example, your application code (loaded by the Application Class Loader) can see and use
java.lang.String(loaded by Bootstrap), but the core Java code cannot see your application classes.Crucially, this prevents malicious code from "spoofing" core Java classes. If you created your own
java.lang.Stringclass and put it on your classpath, the delegation model ensures it will never be loaded. The request forjava.lang.Stringwould go up to the Bootstrap Class Loader, which would find and load the official version first, and the process would stop before ever considering your malicious version.
These two principles are also explained well in the Baeldung article.
This brief section explains the important guarantees provided by the delegation model.
Read subsections 4.2 and 4.3 to understand the importance of class uniqueness and the visibility rule.
Observing the Hierarchy in Code
You can easily verify this hierarchy yourself. The ClassLoader.getSystemClassLoader() method returns the Application loader, and getPlatformClassLoader() returns the Platform loader. Let's see what happens when we ask different classes which loader loaded them.
public class ClassLoaderDemo {
public static void main(String[] args) {
// 1. A class from the application classpath
System.out.println("ClassLoader for this class: " + ClassLoaderDemo.class.getClassLoader());
// 2. A class from a standard Java library (part of the platform)
// For example, classes for XML parsing or logging
System.out.println("ClassLoader for SQL Driver Manager: " + java.sql.DriverManager.class.getClassLoader());
// 3. A core class from java.lang
System.out.println("ClassLoader for ArrayList: " + java.util.ArrayList.class.getClassLoader());
}
}
When you run this code, you'll see output similar to this:
ClassLoader for this class: jdk.internal.loader.ClassLoaders$AppClassLoader@...
ClassLoader for SQL Driver Manager: jdk.internal.loader.ClassLoaders$PlatformClassLoader@...
ClassLoader for ArrayList: null
This output perfectly demonstrates the hierarchy:
ClassLoaderDemois loaded by theAppClassLoader.DriverManageris loaded by thePlatformClassLoader.ArrayListreturnsnullfor its class loader, indicating it was loaded by the native Bootstrap Class Loader.
Conclusion
In this lesson, we've unpacked the structure and logic of Java's class loader subsystem. You now have a solid model for how classes are discovered and brought into the JVM.
Here are the key takeaways:
- Class Loader Hierarchy: Java uses a hierarchy of three main class loaders: Bootstrap (for core runtime), Platform (for standard extensions), and Application (for your code).
- Parent-Delegation Model: When a class is requested, the loader first delegates the search to its parent. It only attempts to load the class itself if the entire parent chain fails.
- Key Guarantees: This model ensures uniqueness (a class is loaded only once) and security (application code cannot replace core Java classes).
We've focused on the built-in class loaders, but the system is extensible. What if you need to load a class from a network location, a database, or a dynamically generated .class file? In our next lesson, we will build upon today's concepts by implementing our own custom class loader to do just that.