The ClassLoader structure
A ClassLoader's basic purpose is to service a request for a class. The JVM needs a class, so it asks the ClassLoader, by name, for this class, and the ClassLoader attempts to return a
Class object that represents the class.
By overriding different methods corresponding to different stages of this process, you can create a custom ClassLoader.
In the remainder of this section, you'll learn about the critical methods of the Java ClassLoader. You'll find out what each one does and how it fits into the process of loading class files. You'll also find out what code you'll need to write when creating your own ClassLoader.
In the next section, you'll put that knowledge to work with our example ClassLoader, the CompilingClassLoader.
ClassLoader.loadClass() is the entry point to the ClassLoader. Its
signature is as follows:
Class loadClass( String name, boolean resolve );
name parameter specifies the name of the class that the JVM
needs, in package notation, such as
resolve parameter tells the method whether or not the class
needs to be resolved. You can think of class resolution as the task of
completely preparing the class for execution. Resolution is not always
needed. If the JVM needs only to determine that the class exists or to find out what its superclass is, then resolution is not required.
In Java version 1.1 and earlier, the
loadClass method is the only
method that you need to override to create a custom ClassLoader. (ClassLoader changes in Java 2
provides information about the
findClass() method available in Java 1.2.)
defineClass method is the central mystery of the ClassLoader. This method takes a raw array of bytes and turns it into a
Class object. The raw array contains the data that, for example, was loaded from the filesystem or across the network.
defineClass takes care of a lot of complex, mysterious, and implementation-dependent aspects of the JVM -- it parses the bytecode format into a run-time data structure, checks for validity, and so on. But don't worry, you don't have to write it yourself. In fact, you couldn't override it even if you wanted to because it's marked as final.
findSystemClass method loads files from the local
filesystem. It looks for a class file in the local filesystem, and if it's there, turns it into a class using
defineClass to convert raw bytes into a
Class object. This is the default mechanism for how the JVM normally loads classes when you are running a Java application. (ClassLoader changes in Java 2
provides details on changes to this process in Java version 1.2.)
For our custom ClassLoader, we'll use
findSystemClass only after we've tried everything else to load a class. The reason is simple: our ClassLoader is responsible for carrying out special steps for loading classes, but not for all classes. For example, even if our ClassLoader loads some classes from a remote Web site, there are still plenty of basic Java libraries on the local machine that must also be loaded. These classes aren't our concern, so we ask the JVM to load them in the default way: from the local filesystem. This is what
The procedure works as follows:
- Our custom ClassLoader is asked to load a class.
- We check the remote Web site, to see if the class is there.
- If it is, fine; we grab the class and we're done.
- If it's not there, we assume that this class is one from the basic Java
libraries and call
findSystemClassto load it from the filesystem.
In most custom ClassLoaders, you would want to call
findSystemClass first to save time spent looking on the remote Web site for the many Java library
classes that are typically loaded. However, as we'll see in the next
section, we don't want to let the JVM load a class from the local
filesystem until we've made sure that we've automatically compiled our
As I mentioned previously, loading a class can be done partially (without resolution) or completely (with resolution). When we write our version of
we may need to call
depending on the value of the
resolve parameter to
findLoadedClass serves as a cache: when
loadClass is asked to load a
class, it can call this method to see if the class has already been
loaded by this ClassLoader, saving the trouble of reloading a class that has already been loaded. This method should be called first.
Let's see how all these methods fit together.
Our example implementation of
loadClass carries out the following
steps. (We won't specify here what special technique will be used to get the
class file -- it might be loaded from the Net, or pulled out of an
archive, or compiled on the fly. Whatever it is, it's the special
magic that gets us our raw class file bytes.)
findLoadedClassto see if we have already loaded the class.
- If we haven't loaded the class, we do special magic to get the raw bytes.
- If we have the raw bytes, call
defineClassto turn them into a
- If we don't have the raw bytes, then call
findSystemClassto see if we can get the class from the local filesystem.
- If the
resolveClassto resolve the
- If we still don't have a class, throw a
- Otherwise, return the class to the caller.
Now that you have a working knowledge of ClassLoaders, it's time to build one. In the next section, we'll bring CCL to life.