开发者

What's the difference between ClassLoader.load(name) and Class.forName(name) [duplicate]

开发者 https://www.devze.com 2023-03-19 16:05 出处:网络
This question already has answers here: Closed 11 years ago. Possible Duplicate: Difference betweeen Loading a class using ClassLoader and Class.forName
This question already has answers here: Closed 11 years ago.

Possible Duplicate:

Difference betweeen Loading a class using ClassLoader and Class.forName

AFAIK, two ways are provided in java to init a class from its name.

  • Class

  • public static Class forName(String className) throws ClassNotFoundException

  • public static Class forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException

  • ClassLoader:

    public Class loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); }

The known thing is in forName method, we can specify the flag of initialize to be false ,this will skip some static things to be initialized for this class. But what's else? And how should I use them correctly?

It's better you can show some good examples.

Thanks!

UPDATE:

After raised question,I made some simple classLoader test.

ClassLoader cls = ClassLoader.getSystemClassLoader(); 
        Class someClass = cls.loadClass("Test");         
        Class someClass0= Class.forName("Test");        
        Class someClass1= Class.forName("Test",false,cls);


        URL[] urls = new URL[] {new File("bin/").toURL()}; 
        ClassLoader cls2 = new URLClassLoader(urls, null); 
        Class someClass2 = cls2.loadClass("Test");         

        ClassLoader cls3 = new URLClassLoader(urls, cls); 
        Class someClass3 = cls3.loadClass("Test");         

       System.out.println(someClass.equals(someClass0));       
       System.out.println(someClass.equals(someClass1));
       System.out.println(someClass.equals(someClass2)); 
       System.out.println(s开发者_JAVA百科omeClass.equals(someClass3));

The result is

true,true,false,true

UPDATE

Here is my answer about
Difference between loadClass(String name) and loadClass(String name, boolean resolve)


Consider this code

class X
{
    static{   System.out.println("init class X..."); }

    int foo(){ return 1; }

    Y bar(){ return new Y(); }
}

The most basic API is ClassLoader.loadClass(String name, boolean resolve)

    Class classX = classLoader.loadClass("X", resolve);

If resolve is true, it will also try to load all classes referenced by X. In this case, Y will also be loaded. If resolve is false, Y will not be loaded at this point.

There doesn't seems to be any good reason for resolve=true. If nobody calls X.bar(), Y will never be needed, why should we load it at this point? And if Y is missing or corrupt, we'll get an error trying to load X, which is totally unnecessary.

Interestingly, this method is protected, so it's not easy to invoke it.

Another method loadClass(name) simply calls loadClass(name,false). It's public, and it takes the sensible choice of resolve=false. So it is exactly what's needed by developers.

ClassLoader only loads classes, it does not initialize classes. We can inspect the class metadata, e.g. its super class, its annotations, its methods and fields, etc. without triggering the static initialization execution. This fact is very important for frameworks.

Now, Class.forName

Basically, Class.forName(String name, boolean initialize, ClassLoader loader) calls loader.loadClass(name). And if initialize=true, the class is initialized - in the X example, we'll see "init class X..." printed.

Class.forName(name) is the same as forName(name, true, currentLoader).

Now, why would anyone want to initialize the class at this point? Wouldn't it be better if the class is initialized only when necessary? A famous use case is JDBC initializing:

    Class.forName("com.mysql.jdbc.Driver");

The convention is, a JDBC driver class registers itself in its static initializer. The above code will trigger the static initialization, making the driver available for subsequent uses.

From today's point of view, that design is really odd. We usually don't rely on static initializers. So there isn't much justification for initialize=true, and Class.forName(name) should be avoided.

A "class literal" will return the class, without initializing it

    Class c = com.mysql.jdbc.Driver.class;
    // actually compiled to
    Class c = Class.forName("com.mysql.jdbc.Driver", false, currentLoader);

Now, what the heck is the "currentLoader"? It is the class loader of the current class

class A
{
    void foo()
    {
        currenLoader == THIS_A_CLASS.getClassLoader()
    }
}

When X.bar() is invoked for the first time, a "Y" class is needed. what's happening is roughly

class X
    bar()
        // new Y();

        Class classY = currentLoader.loadClass("Y");
        Constructor cst = classY.getConstructor();

        // next line will initialize Y (if not yet)
        // creating an instance of a class requires it be initialized
        Object y = cst.newInstance();


ClassLoader.loadClass(String name) will attempt to load the class using the specified classloader. Class.forName(String name) will attempt to load the class using the default system classloader hierarchy.

0

精彩评论

暂无评论...
验证码 换一张
取 消