【问题标题】:What's the difference between ClassLoader.load(name) and Class.forName(name) [duplicate]ClassLoader.load(name) 和 Class.forName(name) 有什么区别 [重复]
【发布时间】:2011-07-10 03:18:57
【问题描述】:

可能重复:
Difference betweeen Loading a class using ClassLoader and Class.forName

AFAIK,java 中提供了两种从类名初始化类的方法。

  • 公共静态类 forName(String 类名)抛出 ClassNotFoundException

  • 公共静态类 forName(String 名称,布尔初始化,类加载器 loader) 抛出 ClassNotFoundException

  • 类加载器

    公共类加载类(字符串 名称)抛出 ClassNotFoundException { 返回负载类(名称,假); }

已知的东西在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));

结果是

真,真,假,真

更新

这是我对
Difference between loadClass(String name) and 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;会造成差异吗?

标签: java classloader


【解决方案1】:

考虑这段代码

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 为真,它还将尝试加载X 引用的所有类。在这种情况下,Y 也将被加载。如果resolve 为false,此时将不会加载Y

resolve=true 似乎没有任何充分的理由。如果没有人调用X.bar(),则永远不需要Y,我们为什么要在此时加载它?如果Y 丢失或损坏,我们将在尝试加载X 时遇到错误,这是完全没有必要的。

有趣的是,这个方法是protected,所以调用起来并不容易。

另一种方法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();

【讨论】:

  • 惊人的答案。太糟糕了,这个问题被标记为重复。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-28
    • 1970-01-01
    • 2018-08-31
    • 2013-03-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多