See this SO article: Difference between thread's context class loader and normal classloader
Important part is that although your classloader is asked to load HelloMain
, it isn't the one doing so, because you simply delegated to the superclass, which delegated to the parent classloader.
Since the parent classloader loaded HelloMain
, all classes referenced by HelloMain
will also be loaded from the parent classloader, and your classloader will not be asked.
Your classloader will only be asked to load referenced classes for classes that was loaded from your classloader.
To explain why, consider two threads with different independent classloaders (A and B). Classloader A doesn't know how to load classes from B, and vice versa.
If thread A creates an object and gives thread B the object, and the JVM then needs to load referenced classes as a result of an action taken by thread B, it wouldn't work if thread B's classloader was asked. So, the classloader of the object is used, not the classloader of the thread.
To show the difference between the thread classloader (called the context classloader) and the classloader that loaded a class, I've updated your code:
public class MyClassLoader extends ClassLoader {
public MyClassLoader() {
super(MyClassLoader.class.getClassLoader());
System.out.println("MyClassLoader()");
}
public MyClassLoader(ClassLoader parent) {
super(parent);
System.out.println("MyClassLoader(" + parent + ")");
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println("MyClassLoader.loadClass(\"" + name + "\")");
return super.loadClass(name);
}
}
public class HelloMain {
public static void main(String[] args) {
System.out.println("Hello from HelloMain");
System.out.println(" Loaded by " + HelloMain.class.getClassLoader());
System.out.println(" Context class loader: " + Thread.currentThread().getContextClassLoader());
Foo foo = new Foo();
foo.sayHello();
Bar bar = new Bar();
bar.sayHello();
}
}
public class Foo {
static {
System.out.println("Foo loaded");
}
public void sayHello() {
System.out.println("Hello from Foo");
System.out.println(" Loaded by: " + getClass().getClassLoader());
System.out.println(" Context class loader: " + Thread.currentThread().getContextClassLoader());
}
}
public class Bar {
static {
System.out.println("Bar loaded");
}
public void sayHello() {
System.out.println("Hello from Bar");
System.out.println(" Loaded by: " + getClass().getClassLoader());
System.out.println(" Context class loader: " + Thread.currentThread().getContextClassLoader());
}
}
Output (using jdk1.8.0_91)
MyClassLoader(sun.misc.Launcher$AppClassLoader@18b4aac2)
MyClassLoader.loadClass("HelloMain")
Hello from HelloMain
Loaded by sun.misc.Launcher$AppClassLoader@18b4aac2
Context class loader: MyClassLoader@6d06d69c
Foo loaded
Hello from Foo
Loaded by: sun.misc.Launcher$AppClassLoader@18b4aac2
Context class loader: MyClassLoader@6d06d69c
Bar loaded
Hello from Bar
Loaded by: sun.misc.Launcher$AppClassLoader@18b4aac2
Context class loader: MyClassLoader@6d06d69c