Java programming language is known for its range of features, providing extensive control to developers. One such feature is the Java ClassLoader, which is part of the Java Runtime Environment that lets developers load Java classes dynamically during runtime to the JVM (Java Virtual Machine).
Table of Contents
Thanks to ClassLoader, the JVM does not need to know about the underlying files or file systems to run Java programs. All these Java classes are not directly loaded into memory all together but only when it is required by an application. This is the primary task of ClassLoader for class loading in Java only when needed.
In this article, we will be discussing different types of built-in Java ClassLoaders, how they work and how to use them.
Before starting with the different types of Java Classloaders, let’s take a look at the code snippet below showing how different ClassLoaders are used for class loading in Java.
1. public void showClassLoaders() throws ClassNotFoundException { 2. System.out.println("this is the Classloader of this class:" + showClassLoader.class.getClassLoader()); 3. System.out.println("Classloader of a class called ArrayList:" + ArrayList.class.getClassLoader()); 4. System.out.println("Classloader of a class called Logging:" + Logging.class.getClassLoader()); 5. }
This will be the output of the above code,
this is the Classloader of this class: sun.misc.Launcher$AppClassLoader@18b4aac2 Class loader of a class called ArrayList:null Class loader of a class called Logging:sun.misc.Launcher$ExtClassLoader@3caeaf62
The example above simply shows three different ClassLoaders application, bootstrap and extension.
An instance of Java.lang.ClassLoader is used to load Java classes. The interesting question is if Java ClassLoader itself is also a class then how is the Java.lang.ClassLoader leaded? The answer is: through Bootstrap ClassLoader.
A Bootstrap ClassLoader is not a Java class, it is a part of the core JVM, written in native code as shown in the above example. It starts the operation when it is called by the JVM. It is responsible to load the first pure Java ClassLoader along with the JDK internal classes, which are in rt.jar format and other core libraries located in $JAVA_HOME/jre/lib directory.
Bootstrap ClassLoader does not have any parent ClassLoader. It serves as a parent of all the other ClassLoader instances.
The Extension ClassLoader is a child of the Bootstrap ClassLoader and it is responsible for the loading of the extensions of the standard core Java classes. It must be available to all applications running on the platform. Extension ClassLoader loads from the JDK extensions directory, $JAVA_HOME/lib/ext directory or it can be loaded from another directory mentioned in the Java.ext.dirs system property.
The System ClassLoader is a child of the Extensions ClassLoader. It is responsible for loading all the application level classes into the JVM. System ClassLoader loads the files present in the classpath environment variable, -classpath or -cp command line option.
As ClassLoader is part of the Java Runtime Environment. Whenever JVM requests a class, the ClassLoader attempts to locate and load its class definition into the runtime using the fully qualified class name.
The Java.lang.ClassLoader.loadClass() method is primarily used for loading the class definition during runtime. In case, if the class is not already loaded, it requests to the parent ClassLoader and this process takes place recursively. In the end, if the parent ClassLoader cannot find the class, then the child class will call Java.net.URLClassLoader.findClass() method to look for classes in the file system by itself.
In the worst-case scenario, even If the last child ClassLoader could not load the class, it throws an exception “Java.lang.NoClassDefFoundError or Java.lang.ClassNotFoundException.”
Here is the example of output showing if the ClassNotFoundException is thrown.
Java.lang.ClassNotFoundException: com.baeldung.classloader.SampleClassLoader at Java.net.URLClassLoader.findClass(URLClassLoader.Java:381) at Java.lang.ClassLoader.loadClass(ClassLoader.Java:424) at Java.lang.ClassLoader.loadClass(ClassLoader.Java:357) at Java.lang.Class.forName0(Native Method) at Java.lang.Class.forName(Class.Java:348)
This was the basic walkthrough of a Java ClassLoader in all possible scenarios. Now, the following are the three extremely important functionality rules followed by every Java ClassLoader.
The Java Virtual Machine and the Java ClassLoader use a specific algorithm to load the classes into the Java file. It is called Delegation Hierarchy Algorithm.
The Java ClassLoader always works upon the operations provided by the delegation model and follows the delegation hierarchy algorithm.
The algorithm is as follows:
Before loading the classes, it is first ensured that the classes are unique and there are no repetitions. It is also made sure that the classes loaded by the parent ClassLoader must not be loaded by the child ClassLoader. If the parent ClassLoader could not find the class, only then the current instance would attempt to do so itself.
Children ClassLoaders are always visible to the classes loaded by their parent ClassLoaders. The classes loaded by the system ClassLoader have visibility into classes loaded by the extension and Bootstrap ClassLoaders but it is not the case vice-versa.
For instance, if a Class 1 is loaded by an application, ClassLoader and Class 2 is loaded by the extensions ClassLoader, then both Class 1 and 2 are visible. Class 2 will be the only class visible as far as other classes loaded by the extension ClassLoader are concerned.
Built-in Java ClassLoader is sufficient in most cases when the files are already present in the file system. However, in some scenarios where we need to load classes from a local hard drive or a network, then built-in class loaders will not be enough. You will require a custom Java ClassLoader to perform such tasks.
A custom Java ClassLoader can do way more than just load the class during runtime, it can be used for modifying an existing bytecode or for creating a class dynamically as per the user’s requirements. You will also require a custom ClassLoader for class versioning while loading different bytecodes for classes with the same names and packages.
Its most common example can be found in browsers. A custom ClassLoader is used in browsers to load executable content from a site. A browser loads applet from different web pages using different ClassLoaders. The applet viewer contains a ClassLoader that accesses the website from a remote server instead of the local file system.
Creating a custom Java ClassLoader is not a complicated task. You just need to extend the ClassLoader class and override the finalClass() method.
In the following example, we have defined a custom ClassLoader that extends the ClassLoader class and loads a byte array from a specified file.
1. public class CustomClassLoader extends ClassLoader { 2. @Override 3. public Class findClass(String className) throws ClassNotFoundException { 4. byte[] a = loadClassFromFile(className); 5. return defineClass(className, a, 0, a.length); 6. } 7. private byte[] loadClassFromFile(String fileName) { 8. InputStream inputStream = getClass().getClassLoader().getResourceAsStream( 9. fileName.replace('.', File.separatorChar) + ".class"); 10. byte[] buffer; 11. ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); 12. int nextValue = 0; 13. try { 14. while ( (nextValue = inputStream.read()) != -1 ) { 15. byteStream.write(nextValue); 16. } 17. } catch (IOException e) { 18. e.printStackTrace(); 19. } 20. buffer = byteStream.toByteArray(); 21. return buffer; 22. } 23. }
Java.lang.ClassLoader class features some very useful methods that make class loading possible. We will further discuss a few important methods from the Java.lang.ClassLoader class and how they work.
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { … }
The loadclass() method loads the class by the given name parameter. The name parameter is the fully qualified name of the class you want to load.
JVM invokes the loadClass() method to resolve class references setting and to determine if the class exists or not. This method acts like an entry point for the class loader.
You can see the loadClass() method code below for a better understanding of the internal working of this method:
1. protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException 2. synchronized (getClassLoadingLock(className)) { 3. //First,it will check in case if the class has already been loaded. 4. Class<?> c = findLoadedClass(className); 5. if (c == null) { 6. long t0 = System.nanoTime(); 7. try { 8. if (parent != null) { 9. c = parent.loadClass(className, false); 10. } else { 11. c = findBootstrapClassOrNull(className); 12. } 13. } catch (ClassNotFoundException e) { 14. // If the class is not found a ClassNotFoundException will be thrown from the non-null parent class loader 15. } 16. 17. if (c == null) { 18. // even if the class is not found, then it will invoke findClass method in order to find the class. 19. c = findClass(className); 20. } 21. } 22. if (resolve) { 23. resolveClass(c); 24. } 25. return c; 26. } 27. }
Default order is followed to load the class:
First, the findLoadedClass() method is invoked to check if the class is already loaded.
Then the loadClass() method is invoked on the parent class loader.
If the class is still not found then the findClass() method is invoked to find the class.
protected final Class<?> defineClass( String name, byte[] b, int off, int len) throws ClassFormatError { … }
The defineClass() method converts an array of bytes into an instance of a class. It is also checked if the converted data did not contain a valid class, it will throw a “ClassFormatError” error.
It is also important to consider that this method cannot be overridden as it is marked as final.
protected Class<?> findClass( String name) throws ClassNotFoundException {…}
As its name suggests, findclass() method finds the class with the fully qualified name as a parameter. We need to override this method in custom classloader implementations that follow the delegation model for class loading in Java.
loadClass() method invokes this method as a last resort if the parent class loader could not find the requested class.
public final ClassLoader getParent() getParent() returns the parent class loader for delegation.
Some implementations of getParent() use null to represent the bootstrap class loader.
public URL getResource(String name)
This method tries to find a resource with the name passed as a parameter. Initially, it will delegate to the parent ClassLoader for the resource but if the parent is null, the path of the ClassLoader built-in JVM is then searched for the resource.
If that fails as well, then the getResource() method will invoke another method called findResource() to find the resource. If resources are found a URL object will be returned by the findResource() method for reading the resource, or null will be returned if the resource could not be found or if the invoker does not have the required privileges.
Java ClassLoader is one of the very powerful features in Java. We have covered all the basic details about Java ClassLoader in the discussion above. You will also find the working of Java ClassLoader, how it is used, the basic classification of ClassLoaders, some important methods in Java.lang.ClassLoader class and how you can create a custom Java loader.
See Also: How to Write a Java Agent
This article could be a good starting point for you if you want to start using Java ClassLoader.
Shaharyar Lalani is a developer with a strong interest in business analysis, project management, and UX design. He writes and teaches extensively on themes current in the world of web and app development, especially in Java technology.
Create a free profile and find your next great opportunity.
Sign up and find a perfect match for your team.
Xperti vets skilled professionals with its unique talent-matching process.
Connect and engage with technology enthusiasts.
© Xperti.io All Rights Reserved
Privacy
Terms of use