Software Development

Get the most out of Java's class loaders

Class loaders are one of Java's most powerful features. Find out why you should use customized class loaders, how to create your own class loaders, and what benefits there are to having a good working knowledge of the class loading mechanism.

Class loaders are one of Java's most powerful features. However, developers often forget about the class-loading component even though it's essential when you develop anything more complex than a "Hello, world" application. Class loader is the class responsible for finding and loading class files at runtime. Java allows you to use different class loaders, as well as your own customized class loader.

A Java program consists of many class files, each of which corresponds to a single Java class. These class files are not loaded into memory all at once like in a static-built C program—they are loaded on demand. This is where the class loader comes into play; it gets platform independent compiled byte code from a source (usually either a .class or .jar file) and loads into JVM memory space so they can be interpreted and executed. By default, each class in an application is loaded by some instance of a java.lang.ClassLoader. You are free to enhance its functionality since it can be easily inherited.

Why you should use customized class loaders

The default java.lang.ClassLoader is only able to load classes from the local filesystem. Java is designed to be flexible enough to get classes from places other than the local hard drive or network, and do special things before actually loading. For example, your application can routinely check a newer version of a plug-in class on a Web site or FTP and automatically verify a digital signature to make sure that only trusted code will be executed. Many well-known application servers use their own sophisticated class loaders.

Get Java tips in your inbox
Delivered each Thursday, our free Java newsletter provides insight and hands-on tips you need to unlock the full potential of this programming language.
Automatically sign up today!

The so-called bootstrap class loader is usually used by default; it is responsible for loading key classes like java.lang.Object and other runtime code located in the rt.jar file into memory. Since the Java Language Specification does not provide any details about the bootstrap class loader (because it has a native implementation), different JVMs may have different behavior of its default class loader. If you have ever seen applets executing on some Web page, you have already used a custom class loader. The applet viewer built in the browser contains a class loader that accesses a Web site on a remote server, loads the raw byte-code files via HTTP, and turns them into classes inside the JVM.

Create your own class loader


Class loaders (except the bootstrap class loader) have a parent class loader, which is basically the class loader instance that loaded the class loader. The most important thing is to correctly set the parent class loader. After that, you can use the getParent() method of the class loader in order to delegate class requests to it (e.g., when your custom class loader cannot find a class using your specialized method). You have to set the parent class loader in the constructor as a parameter for the java.lang.ClassLoader constructor:




public class MyClassLoader extends ClassLoader{ 

      public MyClassLoader(){
            super(MyClassLoader.class.getClassLoader());
      }
}

The method loadClass(String name) is the entry point to our ClassLoader. The name parameter is a fully qualified class name (FQCN), i.e., a class name with package. If the parent class loader is set correctly, when MyClassLoader is requested to load a class by loadClass(String name) and it is unable to find the class, it should ask the parent first. If the parent also cannot find the class, the findClass(String name) method is invoked. The default implementation of findClass(String name) normally throws ClassNotFoundException, an exception which most developers know all too well. Developers of custom class loaders are expected to override this method when they inherit the java.lang.ClassLoader.

The purpose of the findClass() method is to contain all specialized code for MyClassLoader—without duplicating the other code such as calling the system ClassLoader when our loader has failed. In this method, the ClassLoader needs to fetch the byte code from an arbitrary source. Once the byte code is retrieved, the method should call the defineClass() method. It is extremely important which instance of ClassLoader called the method for the loaded class. Thus, if two ClassLoader instances define byte code from the same or different sources, the defined classes are treated as different.

Let's take two similar class loaders MyClassLoader1 and MyClassLoader2, which are capable of finding the byte code of MyCoolClass from the same source. If an application will load two instances of MyCoolClass independently by way of two class loaders (we'll say coolClass1 via MyClassLoader1 and coolClass2 via MyClassLoader2), MyCoolClass.class will be defined for them independently as well. When you execute the following code:





MyCoolClass coolClass1 = (MyCoolClass)coolClass2;

you will get the ClassCastException. (Developers often get this exception if they don't have a good understanding of the class loading mechanism.) Since they are defined by different loaders, the JVM sees two distinct class types. The variables coolClass1 and coolClass2 are not type compatible, although it is of the same class type and loaded from the same source.

Whether you override findClass() or loadClass(), the method getSystemClassLoader() gives you direct access to the system ClassLoader in the form of an actual ClassLoader object. You can also access it implicitly through the findSystemClass(String name) call. The method getParent() allows you to get the parent class loader. Listing A contains an example of a ready-to-run custom class loader.

Summary

If you have a good understanding of the class loading mechanism, it can protect you in the long run from debugging ClassNotFound or ClassCastException errors in the application. This is especially important when an application works inside a third-party application server since such app servers often use proprietary and sophisticated class loaders.

Peter V. Mikhalenko is a Sun certified professional who works for Deutsche Bank as business consultant.

0 comments

Editor's Picks