ClassLoader.load(name)和Class.forName(name)有什么区别?

4

可能是重复问题:
使用ClassLoader和Class.forName加载类的区别

据我所知,在Java中提供了两种通过类名初始化类的方法。

  • 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); }

已知forName方法中,我们可以将initialize标志指定为false,这将跳过一些静态内容的初始化。但还有什么呢?我应该如何正确使用它们?

最好能给出一些好的示例。

谢谢!

更新:

在提出问题后,我进行了一些简单的classLoader测试。

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(someClass.equals(someClass3));

结果是

true,true,false,true

更新

以下是我的答案关于
loadClass(String name)和loadClass(String name, boolean resolve)之间的区别


@Bohemian,我已经阅读了链接,但我没有看到任何人强调Class.forName将初始化静态内容,包括静态块和静态变量的区别。此外,这两种方法实际上最终调用不同的本地方法,private native void esolveClass0(Class c); private static native Class forName0(String name,boolean initialize,ClassLoader loader)throws ClassNotFoundException; 这会造成差异吗? - Clark Bao
2个回答

14

考虑以下代码

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

    int foo(){ return 1; }

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

最基本的API是ClassLoader.loadClass(String name, boolean resolve)

    Class classX = classLoader.loadClass("X", resolve);
如果resolve为true,则它还会尝试加载X引用的所有类。在这种情况下,Y也将被加载。如果resolve为false,则Y将不会在此时加载。
似乎没有任何好理由使用resolve=true。如果没有人调用X.bar(),那么永远不需要Y,为什么要在此时加载它?如果Y缺失或损坏,我们将在尝试加载X时收到错误,这是完全不必要的。
有趣的是,该方法是受保护的,因此很难调用它。
另一个方法loadClass(name)只是调用loadClass(name,false)。它是公共的,并且采取了合理的选择resolve=false。因此,这正是开发人员所需的。 ClassLoader只加载类,不初始化类。我们可以检查类元数据,例如其超级类、注释、方法和字段等,而不触发静态初始化执行。这一事实对于框架非常重要。
现在来看Class.forName 基本上,Class.forName(String name, boolean initialize, ClassLoader loader)调用loader.loadClass(name)。如果initialize=true,则该类将被初始化-在 X的例子中,我们将看到打印了"init class X..."Class.forName(name)等同于forName(name, true, currentLoader)
现在,为什么有人想要在此时初始化类?将类仅在必要时才进行初始化不是更好吗?一个著名的用例是JDBC初始化:
    Class.forName("com.mysql.jdbc.Driver");
约定俗成的是,JDBC驱动程序类在其静态初始化程序中注册自己。上述代码将触发静态初始化,使驱动程序可用于后续使用。
从今天的角度来看,这种设计真的很奇怪。我们通常不依赖于静态初始化程序。因此,initialize=true没有太多的理由,并且应该避免使用Class.forName(name)。
“类文字”将返回类,而无需初始化它。
    Class c = com.mysql.jdbc.Driver.class;
    // actually compiled to
    Class c = Class.forName("com.mysql.jdbc.Driver", false, currentLoader);

现在,"currentLoader"是什么意思呢?它是当前类的类加载器。

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

第一次调用 X.bar() 时,需要一个 "Y" 类。大致发生的情况是

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();

惊人的回答。太遗憾了,这个问题已被标记为重复。 - Kenny Worden

1

ClassLoader.loadClass(String name) 将尝试使用指定的类加载器加载类。Class.forName(String name) 将尝试使用默认的系统类加载器层次结构加载类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接