There are two kinds of class loaders: one is provided by system, another is user-defined. The system provides three class loaders: bootstrap (load JVM core classes), extensions (load JVM extension classes) and system (load classes defined on the classpath). Developers do not need know details of these class loaders, only need to consider user-defined class loaders.
Delegation
Java class loaders uses the proxy pattern. When a class loader tries to find a class, it will delegate this job to its parent class loader first, and its parent class loader will also do the same thing. Finally, use bootstrap class loader to load the Java core classes. This proxy pattern is to guarantee the Java core classes safe. Because when JVM compares two classes, it not only use class full name, it also compares class loaders of these two classes are same or not. So if not use the proxy pattern, Java core classes may be loaded by different class loaders. That will result a same Java core class will be identified as different ones by JVM.
Defining loader and initiating loader
java.lang.ClassLoader has two methods related load class: defineClass and loadClass. The difference is that loadClass method will start the class loading process, but defineClass method will do the load class job finally. The class loader starts the class loading may be different with the class loader finished the class loading. The former called initiating loader, and the latter called defining loader. When JVM compares whether two classes are same with each other, JVM will use the defining loader, other than the initiating loader.
Thread-context class loader
From JDK 1.2, thread-context class loader has been imported. It used to solve that how to load SPI implementation classes. Because SPI interfaces are part of Java core library, so these classes will be loaded by bootstrap class loader, but bootstrap class loader cannot load implementations. Because these implementations are involved at 3rd party libraries. Class loader proxy model cannot solve this problem. With thread-context class loader, this problem can be solved. Because by default, a thread-context class loader is the system class loader, so classes on the classpath can be loaded. And developers can make the thread-context class loader changed in their code to fit the web container or OSGi environments.
Web container
The web container like Tomcat and Jetty defined their own class loaders and also apply the proxy pattern. But different with common class loader, the proxy pattern of the web container class loader is reversed. Because different web apps will use different libraries. But when load the core Java library, the web container class loader use the same order.