ClassLoader.load(name)和Class.forName(name)之间的区别是什么

时间:2011-07-10 03:18:58

标签: java classloader

  

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

AFAIK,在 java 中提供了两种方法来从名称中初始化一个类。

  • public static Class forName(String className)抛出 ClassNotFoundException的

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

  • 类加载器

    public class loadClass(String name)抛出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));

结果是

真,真,假真

更新

以下是关于
的答案 Difference between loadClass(String name) and loadClass(String name, boolean resolve)

2 个答案:

答案 0 :(得分:10)

考虑这段代码

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 literal”将返回该类,而不进行初始化

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

答案 1 :(得分:1)

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