0
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.

Neha
  • 745
  • 4
  • 10
  • 18
  • https://stackoverflow.com/questions/32919494/class-fornamename-newinstance-vs-name-class-newinstance-difference-in-us – guleryuz May 01 '18 at 18:20
  • Check if `c1` and `testMe.class` is the same thing. – lexicore May 01 '18 at 18:20
  • If you already have java class then you could do `testMe.class.getDeclaredConstructors()` but if you have only string name of a class like `"a.b.c.d.MyClass"` for example from config file then you first need to "convert" it to java object of type `Class` with `Class c1 = c.loadClass("a.b.c.d.MyClass")` – Ivan May 01 '18 at 18:58

1 Answers1

1

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

Slaw
  • 37,820
  • 8
  • 53
  • 80
  • So the short answer to the question is that writing "test.testme.class" has the same end result as calling loadClass("test.testme"). It's just two different ways to get the classloader to load a class and return to you a Class object representing that class. – user1676075 May 01 '18 at 21:57