skip to content

Walk through, step by step, what a standard Java class loader does when it is asked to load a type it has not loaded before. Which loader ends up actually defining a core JDK type such as java.util.List?

level: juniorimportance: must knowfreq 60%

answer

  1. findLoadedClass → parent.loadClass → findClass
  2. Up first, search on the way back down
  3. null parent == bootstrap loader
  4. bootstrap → platform → application
  5. Core JDK types always defined once, by bootstrap

basics

~20 s

It first checks whether it has already loaded that name; if not, it asks its parent, which asks its own parent, all the way up to the bootstrap loader. Only if every ancestor fails does it try to find the bytes itself. Core JDK types are therefore always defined by the bootstrap loader.

solid answer

~60 s

The algorithm lives in `ClassLoader.loadClass` and has three steps: 1. **Check already-loaded.** `findLoadedClass(name)` — if this loader has already defined or recorded that name, return it. A loader never loads the same name twice. 2. **Delegate upward.** Call `parent.loadClass(name)`. If the parent is `null`, that means the bootstrap loader, and the JVM asks it directly. The parent runs the same three steps, so the request travels all the way to the top of the hierarchy before anyone searches. 3. **Find it yourself.** Only if the whole ancestor chain throws `ClassNotFoundException` does this loader call its own `findClass(name)`, read the bytes from wherever it gets them, and call `defineClass`. So the search order is *up first, then locally* — the opposite of what "my classpath" intuition suggests. For `java.util.List`, the request from an application loader travels up through the platform loader to the bootstrap loader, which finds it in the runtime image and defines it. The application loader never even looks. That is why every part of the application shares one `java.util.List` type.

code

java · 7 lines
java
System.out.println(java.util.List.class.getClassLoader());   // null  -> bootstrap
System.out.println(javax.sql.DataSource.class.getClassLoader()); // platform loader
System.out.println(MyService.class.getClassLoader());          // app (system) loader

ClassLoader app = ClassLoader.getSystemClassLoader();
System.out.println(app.getParent());            // platform loader
System.out.println(app.getParent().getParent()); // null -> bootstrap

go deeper

for a junior

Recite the three steps and the three built-in loaders in order, and know that core JDK classes report a null loader because bootstrap defined them.

for a middle

Explain that the recursion reaches the top before any searching, that a parent's ClassNotFoundException is normal control flow, and that loading precedes linking and initialisation.

for a senior

Add the practical consequences — one definition per name per chain, per-name locking on parallel-capable loaders, and how you would verify at runtime which loader defined a type.

for a principal

Frame delegation as the mechanism that guarantees a single agreed definition of shared types across a process, which is the property every plugin or container architecture must consciously preserve or deliberately break.

## The hierarchy the request travels through A class loader is an object with a *parent* reference, forming a chain (not a class-inheritance hierarchy — a delegation chain of instances). In a modern JDK the built-in chain is: - **Bootstrap loader** — part of the VM itself, written in native code, represented in Java as `null`. Defines the core types of the runtime image: `java.lang.*`, `java.util.*`, and so on. - **Platform loader** — its parent is the bootstrap loader. Defines the remaining JDK modules that are not part of the core (it replaced the older "extension" loader in JDK 9). - **Application (system) loader** — its parent is the platform loader. Loads the classes of your application from the class path or module path. `ClassLoader.getSystemClassLoader()` returns it, and it is the default loader for user code. Any custom loader you write sits below one of these, with a parent you supply (defaulting to the system loader). ## The algorithm, precisely `java.lang.ClassLoader.loadClass(String name, boolean resolve)` implements it, and its shape has been stable for decades: ```java protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { Class<?> c = findLoadedClass(name); // 1. already loaded? if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); // 2. delegate up } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException ignored) { // parent could not find it — that is expected, fall through } if (c == null) { c = findClass(name); // 3. find it myself } } if (resolve) resolveClass(c); return c; } } ``` Three things are worth reading carefully: **The recursion happens before any searching.** Step 2 calls the parent's `loadClass`, which does its own steps 1–3. So the request climbs all the way to the bootstrap loader *first*, and searching happens on the way back down. The bytes are found by the highest loader in the chain that can find them. **A failed parent lookup is normal control flow.** `ClassNotFoundException` from the parent is caught and swallowed; it simply means "not mine, keep going." That is why a broken custom loader that lets this exception escape breaks loading entirely. **A `null` parent means bootstrap, not "no parent."** Because the bootstrap loader is native, it has no Java object; `getClassLoader()` returning `null` on a core JDK class means "loaded by bootstrap," not "loaded by nobody." **The lock is per class name** on loaders registered as parallel-capable, so two threads loading different names do not serialise on one loader-wide lock. ## Loading versus initialisation This question is about *finding and defining* the type. Loading produces a `Class` object from bytes; the separate linking and initialisation steps that follow are what run static initialisers. Being precise about that boundary is a good signal in an interview: "the delegation algorithm decides *who defines the type*; running its static setup is a later, separate step." ## Which loader defines java.util.List Suppose your application code triggers loading of `java.util.List`. The application loader is asked; it has not loaded it, so it asks the platform loader; the platform loader asks the bootstrap loader; the bootstrap loader finds `java.util.List` in the runtime image and defines it. The `Class` object is returned back down the chain, and the application loader records it as visible to itself without having defined it. Call `List.class.getClassLoader()` and you get `null`, confirming bootstrap defined it. Call `MyService.class.getClassLoader()` and you get the application loader. The consequence is the one that matters: no matter how many loaders exist in the process, `java.util.List` is defined exactly once by exactly one loader, so every component in the process agrees on that type. That single-definition property is the whole reason the algorithm delegates upward instead of searching locally. ## Common phrasings of the same question "What is the parent delegation model?", "Walk me through `ClassLoader.loadClass`", "Which loader loads `String`?" — all the same content. Answer with the three steps, name the three built-in loaders in order, and state that `null` means bootstrap.

  • What does it mean when getClassLoader() returns null for a class?
    It means the class was defined by the bootstrap loader. The bootstrap loader is implemented inside the VM in native code and has no corresponding Java object, so the API represents it as null. It is not an error and does not mean the class was loaded by nobody. It is also why code that walks the parent chain must treat null as the top of the chain rather than as a missing parent.
  • Where does the delegation algorithm fit relative to linking and initialisation?
    Delegation only decides who finds the bytes and calls defineClass, producing a Class object. After that the JVM links the class — verification, preparation of static fields with default values, and resolution of symbolic references — and initialises it, which is when static initialisers and static field assignments actually run. So a class can be loaded long before it is initialised, and the delegation model has nothing to say about when that initialisation happens.

A junior employee asked a question always checks with their manager first, who checks with theirs, up to the CEO. Only if nobody above knows the answer does the junior go look it up themselves — so the most authoritative answer in the building always wins, and everyone quotes the same one.

saying these in an interview costs you the question

  • Saying a loader searches its own class path first and only asks the parent on failure — that is the inversion, not the default
  • Treating a null class loader as an error rather than as the bootstrap loader
  • Describing the parent relationship as Java class inheritance rather than a per-instance parent reference
  • Claiming delegation runs static initialisers — loading is separate from initialisation
  • Believing the extension class loader still exists in modern JDKs; the platform loader replaced it in JDK 9

context