Search code examples
javadata-structuresjava-streamclassloader

what is the difference between loadClass(("fully qualified class name") and <ClassName>.class.getDeclaredConstructors


class testMe{

    void show(){
        System.out.println("Hello");
    }

}

public class ClassloadersExample {
    public static void main(String args[]) {
        ClassLoader c = ClassloadersExample.class.getClassLoader(); //Line 1
        try {
            Class c1 = c.loadClass("test.testMe"); // Line 2
            Constructor a[] = c1.getDeclaredConstructors(); 
            for (Constructor constructor : a) {
                testMe m = (testMe)constructor.newInstance();
                m.show();
            }

            Constructor con[] = testMe.class.getDeclaredConstructors(); // Line 6
            for (Constructor constructor : con) {
                constructor.setAccessible(true);
                testMe t = (testMe)constructor.newInstance();
                t.show();
            }
        }
        catch(Exception e){
            System.out.println("exception");
        }

    }
}

I am testing above code. Both give me the same result. I am trying to understand the difference between line 1,2 and line 6. I can achieve the same result by both the approaches.


Solution

  • Answer

    There is no functional difference. As you have discovered, there are various ways to get a Class object and to instantiate instances of that class. They all lead to the same result, however.

    In general, until needed otherwise, always:

    • Use class literals or getClass() for obtaining a Class
      • Example 1: Class<Foo> cl = Foo.class;
      • Example 2: Class<? extends Foo> cl = fooInstance.getClass();
    • Use the new keyword for instantiating instances
      • Example: Foo f = new Foo();
      • Caveat: Sometimes the API is designed using the Builder Pattern, the Factory Method Pattern, etc... If this is the case then you'll have to use those methods. Internally, the builders and factory methods might even use the new keyword.

    Explanations and Minor(?) Differences

    Classes

    Off the top of my head, these are the ways I can think of to get a Class object:

    1. Using class literals
      • Class<Foo> cl = Foo.class;
    2. Calling getClass() on an instance
      • Class<? extends Foo> cl = fooInstance.getClass();
    3. Calling Class.forName(String)
      • Class<?> cl = Class.forName("some.package.Foo");
      • This is shorthand for Class.forName("some.package.Foo", true, currentClassLoader)
    4. Calling ClassLoader.loadClass(String)
      • Class<?> cl = classLoader.loadClass("some.package.Foo");
      • Won't necessarily load the Class. If the Class has already been loaded then that loaded instance will be returned.

    All the above will get the Class object that represents some.package.Foo. In all likelihood (I'm not 100% certain), methods 1, 2, and 3 all end up delegating to method 4 eventually.

    You'll notice that the generic signatures of the Class objects (the <> parts) are different depending on the way you get the Class. Methods 1 and 2 know what type the Class will be at compile-time and so can return a Class with the appropriate generics. Whereas methods 3 and 4 have no idea what type the Class will represent at runtime and therefore return a Class<?> (? is a wildcard).

    Something to note about method 3. As I mentioned above, Class.forName(String) is shorthand for Class.forName(String, boolean, ClassLoader) where the boolean will be true and the ClassLoader will be the current ClassLoader. The boolean parameter determines whether or not the Class is initialized. To initialize a class means (among other things?) initializing all the static variables and running the static initializers. So while methods 1, 2, and 4 will not initialize the Class, method 3 will. If you don't want method 3 to initialize the Class you'll need to use the longer version and make the boolean parameter false.

    The link in the question comments talk about why you would use methods 3 or 4.

    Creating Instances

    Once again off the top of my head, these are the ways I can think of to instantiate objects:

    1. Using the new keyword
      • Foo f = new Foo();
    2. Using Class.newInstance()
      • Foo f = fooClass.newInstance();
      • Requires that the class has a no-arg constructor
      • Deprecated since Java 9 in favor of using Constructor objects
    3. Using one of the Constructor objects
      • Foo f = fooClass.getConstructor().newInstance();

    The major difference here is how each method creates an instance. The first method simply uses the new keyword. The 2nd and 3rd methods use reflection. Reflection is useful when you don't know the types at compile-time but should be avoided until needed.

    Method 3 uses Class.getConstructor(Class<?>... paramterTypes). Since I'm passing an empty array of parameter types the returned Constructor is a no-arg constructor. This would fail if the class does not have a no-arg constructor.

    Your use of getDeclaredConstructors() simply returns all the constructors and then you pick the one you want and call newInstance. The example I gave in 3 bypasses this by going straight for the public no-arg constructor. Using getDeclaredConstructors() will also give you the non-public constructors (i.e. protected, package, and private). This can allow you to call a non-public constructor you wouldn't otherwise be able to. But this is only if you have the available access to call the constructor; you'll need things like permission from any installed SecurityManager and (if using Java 9+) the package that the class is in must be reflectively-accessible (opens) to your module.

    Some Links