Notes about Java Class Loaders
- The system class loader loads classes where it can find them. This is typically defined by the classpath setting at the environment level. A custom class loader can extend this by sub classing the default class loader and by allowing the user to specify a series of locations or jar files that can be searched to locate a class. This provides more flexibility than the standard class loader.
- Most custom class loaders will provide a cache so that searching for a class is faster. The cache is typically a HashMap with the name of the class being the key. The constructed class is stored against the key name. To construct a class is relatively expensive and is performed by the defineClass() method. This method takes an array of bytes and returns a constructed class. The constructed class is registered with the JVM, which means you can't call defineClass() on the same class name by the same classLoader twice or you will get a duplicate class definition exception. There is no other mechanism to return a constructed class from an array of bytes. The array of bytes is easy enough to read from the file, the construction of the class is done at the JVM level. That is so that the checks for linkages, security and other class related activities can be achieved.
- A class "signature" is made up of the classLoader that loaded the class and the actual class name. That means is you load a class from a custom class loader it will have a different class "signature" to the class "signature" generated by the system class loader or by another custom class loader instance. The only way I've found to dynamically refresh a class is to create an instance of a custom class loader, load the class, change the class file by modifying the code and recompiling, copying the compiled class file to a place where the custom class loader can find it, recreate the instance of the class loader and then reload the class. In this way, the class "signature" of the second instance of the class will be different to the "signature" of the first instance of the class as they used a different instantiation of the class loader.
- You can't cast an object across class loaders. That means if you load an object into a custom class loader it will NOT be the same reference to the class from the original class loader. If you consider the previous point, each of these classes will have a different "signature" therefor would not be compatible. That is, assuming all classes are visible, this is not allowed:
// assume we are in classLoaderA which may be the
// system class loader.
// the TestClass reference is therefore relative to
// create a new class loader
MyClassLoader classLoaderB = new MyClassLoader();
// the class that is returned from the following is
// relative to classLoaderB but the cast class is
// relative to classLoaderA.
// This will cause a class cast exception
test = (TestClass) classLoaderB.loadClass("TestClass");
If you need to use classes across classLoaders you have to use interfaces.