7

I have one question regarding concepts of class loading. How to load a .class file twice in JVM. I am also writing an excerpt of code that I have written to accomplish this..

1) Loader 1 code

public class MyClassLoader extends ClassLoader {

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

    public Class loadClass(String classname){
        try {
            return super.loadClass(classname);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}

2) Loader 2 code

public class AnotherClassLoader extends ClassLoader {

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

    public Class loadClass(String classname){
        try {
            return super.loadClass(classname);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}

3) Now i am loading a class named A using this two different class loaders. I suppose the operation classA1==newClassA should return false. Here is the code:

public static void main(String[] args) {
        MyClassLoader loader1 = new MyClassLoader();
        AnotherClassLoader newLoader = new AnotherClassLoader();
            System.out.println("Load with Custom Class Loader instance");
            Class classA1 = loader1.loadClass("com.hitesh.coreJava.A");
            System.out.println("Class Loader:::"+classA1.getClassLoader());
            Class newClassA = newLoader.loadClass("com.hitesh.coreJava.A");
            System.out.println("Class Loader:::"+newClassA.getClassLoader());
            System.out.println(classA1==newClassA);
            System.out.println(classA1.hashCode() + " , " + newClassA.hashCode());

    }

4) Result of executing above code:

Load with Custom Class Loader instance Class Loader:::sun.misc.Launcher$AppClassLoader@11b86e7 Class Loader:::sun.misc.Launcher$AppClassLoader@11b86e7 true 1641745 , 1641745

Could you please explain this

Wojciech Wirzbicki
  • 3,887
  • 6
  • 36
  • 59
Hitesh
  • 703
  • 1
  • 9
  • 14

3 Answers3

9

Try this

public class Test1 {

    static class TestClassLoader1 extends ClassLoader {

        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            if (!name.equals("Test1")) {
                return super.loadClass(name);
            }
            try {
                InputStream in = ClassLoader.getSystemResourceAsStream("Test1.class");
                byte[] a = new byte[10000];
                int len  = in.read(a);
                in.close();
                return defineClass(name, a, 0, len);
            } catch (IOException e) {
                throw new ClassNotFoundException();
            }
        }
    }


    public static void main(String[] args) throws Exception {
        Class<?> c1 = new TestClassLoader1().loadClass("Test1");
        Class<?> c2 = new TestClassLoader1().loadClass("Test1");
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c1 == c2);
    }
}

output

class Test1
class Test1
false
Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
  • 3
    The above code snippet is correct. It is true that any class loaded into the JVM is identified by the(package,className,Class Loader Name). Thus it is possible to load a class twice in JVM by using two different instances of class loader. For this you need to call defineClass() explicitly in your Custom Class Loader code as this method checks at run time if two different instances are loading the class.. – Hitesh Apr 01 '13 at 06:37
  • But the static initializers (if placed within Test1) will run only once. If the class is loaded (and later initialized) twice, shouldn't the static initializers be run twice? – TheLostMind Sep 19 '14 at 09:42
7

Both classloaders start the lookup in their parent classloader (that's what the super() call is about). So actually the super classloader loads it in both cases.

You can try this:

String pathToJar = "C:\\path\\to\\my.jar";
String className = "com.mypackage.ClassA";
URLClassLoader cl1 = new URLClassLoader(new URL[] { new URL(pathToJar) });
URLClassLoader cl2 = new URLClassLoader(new URL[] { new URL(pathToJar) });
Class<?> c1 = cl1.loadClass(className);
Class<?> c2 = cl2.loadClass(className);
System.out.println(c1);
System.out.println(c2);
System.out.println(c1==c2 ? "Parent classloader loads" : "Parent classloader does not load");
cl1.close();
cl2.close();

Make sure that my.jar is NOT on your classpath.

gaborsch
  • 15,408
  • 6
  • 37
  • 48
  • If you want to produce some difference, try instantiating a URLClassLoader, with a jar file that is not on your default class path. If you do it twice, the loaded classes will be different. – gaborsch Jan 10 '13 at 12:00
3

In both cases you are using the same ClassLoader to perform the loading. You have two ClassLoaders but each just call super.loadClass() which delegates to the same parent ClassLoader which is AppClassLoader.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Even if we make two class loaders return two different class objects of the same class, why the static initialiser is executed only once? – Pushparaj Jan 31 '20 at 13:21
  • 1
    @Pushparaj A class is loaded/initialised for each ClassLoader if it is a different class. If they both have the same parent/super class than that super is loaded once. – Peter Lawrey Feb 13 '20 at 18:46