ClassLoaders sound intimidating, but if you look at the
source code for one, they are pretty simple. Basically all they do is find the class
file somewhere on disk, read it into RAM (Random Access Memory),
and call java.lang.ClassLoader.defineClass tell the
system to treat ram image as legitimate byte codes. defineClass and all the other interesting methods are protected, so
you will have to write you own class that extends ClassLoader to get at them. It will all come clear if you study the
source code for ClassLoader in src.zip.
There are some minor complications dealing with chaining ClassLoaders so they ask other ClassLoaders
first before going to look on their own.
The default ClassLoader can drive you nuts. Sometimes
it refuses to look on the local hard disk CLASSPATH. It
won’t look in a jar other than the ones mentioned in the <APPLET ARCHIVE ones. These archives are downloaded en masse whether
you need the files in them or not. The ClassLoader/Security system won’t let net-loaded classes use
pre-installed local DLLs (Dynamic Link Libraries). Unsigned Applets cannot
use custom ClassLoaders.
Custom ClassLoaders
A custom ClassLoader is really quite simple. It need not even involve writing a
new class. You can often just instantiate one of the existing ClassLoaders. A ClassLoader needs a
private byte[] loadClassData(String name) method to find
the bytes for the class somewhere and a standard protected
synchronized Class loadClass(String name, Boolean resolve) to call your
loadClassData. You inherit everything else you need from
ClassLoader.
Why would you ever want a custom ClassLoader?
- They let you change code on the fly without stopping the application. See more
detail on hot code switching below.
- They let you deal with on-the-fly
byte code generated in RAM. If you generate some byte codes in
RAM and want to
execute them, you can do it like this without first creating a
- They let you deal with byte code served from a database, local or remote.
- Loading code from a cache. The code may come from a cache, or if it is not
there from a remote server.
- They let you create a sandbox to control exactly what classes are
loadable.
- Discourage piracy by dynamically loading frequently-changing small code
snippets over the web while serving the bulk of the app from hard disk.
- They let you deal with byte code coming from an unusual source.
- They let you dynamically change the classpath.
- They help with garbage collection. The static variables in a class and the code
itself will live for the life of the app if loaded by the default ClassLoader. If they were loaded by a custom ClassLoader they are eligible for garbage collection once all
references to the custom ClassLoader and class are
gone.
- Say you have inherited some code that uses a singleton to access a database.
Now you want to use more than one database at once. You could go through and
replace all the singleton accesses with a multi-access design. Or you could have
two ClassLoader instances, each one configured for a
particular database.
- If you’ve got an intractable memory packratting problem there’s
probably some static variables involved. If you drop the ClassLoader referencing the static variables, the leaked memory can
come back. You can’t drop the instance of the default ClassLoader, but you can drop a custom one.
- Let you encrypt your code to obfuscate it.
- Let you store your code in some non-standard form of jar.
Hot Code Changes
ClassLoaders let you
modify code on the fly without shutting down an application. New code is loaded with
Class.forname. It creates new versions of the objects by
reloading the classes for the objects with a ClassLoader.
You then have both old and new versions of the same object, both with the same name,
but with different data and different code. From the JVM (Java Virtual Machine)
’s point of view, a class loaded with a new ClassLoader is a different animal entirely from the one loaded with
the standard ClassLoader even if the classes have the same
name. You can manipulate both old and new objects with a common interface.
With a new ClassLoader, you can load a different
version of a class. The old objects continue to use the old code. New objects use the
new code. A given ClassLoader can load a given class only
once. There is no need to unload a class. When the objects using it are no longer
referenced, the class object itself, along with the code, will be garbage collected.
The same classes, loaded by different class loaders are considered distinct classes.
They are not instanceofs each other!
You will have to instantiate a new ClassLoader every
time you have a new generation of classes. You can load all the replacement classes
of a generation with the same ClassLoader. However, when
you want to replace the replaced classes, you need a yet another new ClassLoader. You can do this with multiple instances of the same
ClassLoader. You only need to write one ClassLoader, perhaps not even one, not one for each generation.
Have a look at the java.net.URLClassLoader. You may
find for your given problem you don’t even have to write whole new ClassLoader, just instantiate one of Oracle’s.
When using Applets or Java Web Start, sometimes Thread.
currentThread(). getContextClassLoader(). getResource()
works better than this. getClass(). getClassLoader().
getResource(). This may bypass a bug in
Java version 1.4 and 1.5 now fixed in 1.6+.
Learning More
Oracle’s Javadoc on
ClassLoader class : available:
Oracle’s Javadoc on
Thread.getContextClassLoader : available:
Oracle’s Javadoc on
URLClassLoader class : available:
Oracle’s Javadoc on
Class class : available: