zoukankan      html  css  js  c++  java
  • Class.forName和ClassLoader.loadClass的比较

    Class.forName和ClassLoader.loadClass的比较

    Class的装载分了三个阶段,loading,linking和initializing,分别定义在The Java Language Specification的12.2,12.3和12.4。
    Class.forName(className)实际上是调用Class.forName(className, true, this.getClass().getClassLoader())。注意第二个参数,是指Class被loading后是不是必须被初始化。
    ClassLoader.loadClass(className)实际上调用的是ClassLoader.loadClass(name, false),第二个参数指出Class是否被link。
    区别就出来了。Class.forName(className)装载的class已经被初始化,而ClassLoader.loadClass(className)装载的class还没有被link。
    一般情况下,这两个方法效果一样,都能装载Class。但如果程序依赖于Class是否被初始化,就必须用Class.forName(name)了。
    例如,在JDBC编程中,常看到这样的用法,Class.forName("com.mysql.jdbc.Driver"),如果换成了getClass().getClassLoader().loadClass("com.mysql.jdbc.Driver"),就不行。
    为什么呢?打开com.mysql.jdbc.Driver的源代码看看,
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
    原来,Driver在static块中会注册自己到java.sql.DriverManager。而static块就是在Class的初始化中被执行。所以这个地方就只能用Class.forName(className)。
     
    看了虚拟机的一些文章,有点感触,赶紧写出来。 

    Class 用来描述一个类型,当一个类型被装载(虚拟机读取二进制的class文件)的时候,会创建一个与该类型对应的Class对象到内存堆中。(这个时候该类型甚至没有被分配内存,设置默认值,更别说初始化了)。 
    Class.forName有2个重载的方法: 
    Java代码  收藏代码
    1. public static Class<?> forName(String name, boolean initialize,  
    2.                ClassLoader loader)  
    3.        throws ClassNotFoundException  
    4.   
    5.   
    6. public static Class<?> forName(String className)   
    7.                throws ClassNotFoundException  

    第一个方法: 
    name:类的全限定名,如:com.aaa.bbb.ccc 
    initialize:如果为true,则会在返回Class对象之前,对该类型做连接,校验,初始化操作。(如:执行static块中的代码) 
    loader:用自定义的类加载器来请求这个类型;当然,你也可以传入null,用bootstrap加载器。 
    第二个方法: 
    name和上面相同。默认initialize为true, 
    而loader是这么获取的 
    Java代码  收藏代码
    1. ClassLoader.getCallerClassLoader()  

    其实就是获取当前类的classLoader,也就是装载执行forName操作的类的classLoader 

    类ClassLoader包括2个重载的loadClass类 
    Java代码  收藏代码
    1. protected synchronized Class<?> loadClass(String name, boolean resolve)  
    2.     throws ClassNotFoundException  
    3.   
    4. public Class<?> loadClass(String name) throws ClassNotFoundException   

    两个方法都需要传name,既类的全限定名作为参数。 
    resolve:表示是否需要连接该类型。 
    注意:是连接(这里面包括校验class文件,准备分配内存,类型常量池的替换),并不会初始化该类型。 

    看源码可以看到,resolve也只是调用了一下resolveClass这个方法 

    Java代码  收藏代码
    1. if (resolve) {  
    2.     resolveClass(c);  
    3. }  


    都说清楚了,也很容易区分出不同点: 
    1.Class.forName返回的Class对象可以决定是否初始化。而ClassLoader.loadClass返回的类型绝对不会初始化,最多只会做连接操作。 
    2.Class.forName可以决定由哪个classLoader来请求这个类型。而ClassLoader.loadClass是用当前的classLoader去请求。 

    举个例子吧: 
    JDBC的注册驱动大家都狠熟悉吧: 
    Java代码  收藏代码
    1. Class.forName(".......")  

    为啥是要用Class.forName? 
    首先看看注册驱动是什么意思。 
    打开DriverManager的代码,可以看到这个方法: 
    Java代码  收藏代码
    1. public static synchronized void registerDriver(java.sql.Driver driver)  
    2. throws SQLException   

    如果要注册驱动,必须得这么来 
    Java代码  收藏代码
    1. DriverManager.registerDriver("......")  

    而这行代码,都是写在驱动的实现类,而且是一个static块来加载的。 
    所以随便找一个jdbc驱动程序的代码,肯定会有这样的实现 
    Java代码  收藏代码
    1. static{  
    2. .......  
    3. DriverManager.registerDriver("......")  
    4. .......  
    5. }  

    用来注册驱动程序到DriverManager,这样驱动才能使用。 
    由于static块是属于类的,所以,此处仅仅需要初始化类即可。不需要具体实例。 
    那么用 
    Java代码  收藏代码
    1. Class.forName(".....")  

    再合适不过了。 
    而ClassLoader由于不会初始化类。明显不行。 
  • 相关阅读:
    迭代器生成器
    eval()
    【剑指offer】调整数组数字位置
    【剑指offer】二进制中1的个数
    【剑指offer】斐波那契数列非递归求解第N项
    【剑指offer】两个栈实现队列
    【剑指offer】逆序输出链表
    【剑指offer】字符串替换
    【剑指offer】规则二维数组查找
    Java转型
  • 原文地址:https://www.cnblogs.com/handsome1013/p/5305058.html
Copyright © 2011-2022 走看看