Java类加载一:Class.forName()和ClassLoader.getSystemClassLoader().loadClass()区别

来源:互联网 发布:反馈神经网络算法 c 编辑:程序博客网 时间:2024/06/05 03:42

要想搞清楚这两个方法的区别,我们需要了解一下Class的加载过程。Class的加载过程分为三步:

  1. loading(装载)
  2. linking(链接)
  3. initializing(初始化)

大家可以通过这篇文章:Java魔法堂:类加载机制入了个门来了解类的详细加载过程。阅读以上文章后,我们一起分析一下两个方法的区别,如有不正之处,欢迎批评指正。

1、forName()

Class.forName()有两个重载的方法,都是public方法。

public static Class<?> forName(String className)public static Class<?> forName(String name, boolean initialize, ClassLoader loader)

public static Class

@CallerSensitivepublic static Class<?> forName(String className) throws ClassNotFoundException {    Class<?> caller = Reflection.getCallerClass();    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);}

public static Class

public static Class<?> forName(String name, boolean initialize, ClassLoader loader)    throws ClassNotFoundException{    Class<?> caller = null;    SecurityManager sm = System.getSecurityManager();    if (sm != null) {        // Reflective call to get caller class is only needed if a security manager        // is present.  Avoid the overhead of making this call otherwise.        caller = Reflection.getCallerClass();        if (loader == null) {            ClassLoader ccl = ClassLoader.getClassLoader(caller);            if (ccl != null) {                sm.checkPermission(                    SecurityConstants.GET_CLASSLOADER_PERMISSION);            }        }    }    return forName0(name, initialize, loader, caller);}

通过对比我们发现两个方法最终都调用了私有的forName0'方法,而'Class.forName(String className)方法中initialize参数默认为trueClass.forName(String name, boolean initialize, ClassLoader loader)方法的initialize参数由用户来指定。javadoc中关于该方法的initialized参数的说明如下:

@param initialize whether the class must be initialized参数initialize 表示该类是否必须被初始化

通过initialize参数我们可以发现,该参数控制了类加载过程的第三步(初始化),该参数在’Class.forName(String className)方法中默认值为true`,因此在类加载的过程中会初始化类的相关信息,比如类中的静态块会被执行。因此我们得出结论:

Class.forName(className)

等同于

initialize = true;Class.forName(className, initialize, loader)

示例代码:
ClassLoaderDemo1代码:

package com.ips.classloader;public class ClassLoaderDemo1 {    public static void main(String [] args){        try {            ClassLoader system = ClassLoader.getSystemClassLoader();            Class<Config> cls = null;            System.out.println("----------方法1----------");            cls = (Class<Config>)Class.forName("com.ips.classloader.Config");            System.out.println("----------方法2----------");            cls = (Class<Config>)Class.forName("com.ips.classloader.Config", false, system);            System.out.println("----------方法3----------");            cls = (Class<Config>)Class.forName("com.ips.classloader.Config", true, system);        } catch (ClassNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}

Config代码:

package com.ips.classloader;public class Config {    private String name;    private static boolean flag;    static {        flag = false;        System.out.println("flag 的值为:" + flag);    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

我们通过三种方式加载Config类,在Config类中有一段静态代码块,通过println函数来判定静态代码块是否被执行。
执行结果如下:

----------方法1----------flag 的值为:false----------方法2--------------------方法3----------

我们发现方法3没有输出flag的值,这是为什么呢?原因是类加载过程中的缓存机制,由于方法1已经加载了该类,因此方法3不会再次加载该类,所以没有输出flag值,为了测试缓存的问题,我们将方法1与方法3的位置互换,程序的执行结果如下,可以看到方法3加载了该类,并且输出去了flag值,而方法1没有输出flag值。我们每次修改完代码都需要重启JVM来执行新的代码也是由类加载的缓存机制造成的。

----------方法3----------flag 的值为:false----------方法1--------------------方法2----------

2、loadClass()

ClassLoader.getSystemClassLoader().loadClass()有两个重载方法,一个public方法,一个protected方法。

public Class<?> loadClass(String name) //方法1protected Class<?> loadClass(String name, boolean resolve)  //方法2

public Class

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

protected Class

protected Class<?> loadClass(String name, boolean resolve)    throws ClassNotFoundException{    synchronized (getClassLoadingLock(name)) {        // First, check if the class has already been loaded        Class c = findLoadedClass(name);        if (c == null) {            long t0 = System.nanoTime();            try {                if (parent != null) {                    c = parent.loadClass(name, false);                } else {                    c = findBootstrapClassOrNull(name);                }            } catch (ClassNotFoundException e) {                // ClassNotFoundException thrown if class not found                // from the non-null parent class loader            }            if (c == null) {                // If still not found, then invoke findClass in order                // to find the class.                long t1 = System.nanoTime();                c = findClass(name);                // this is the defining class loader; record the stats                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);                sun.misc.PerfCounter.getFindClasses().increment();            }        }        if (resolve) {            resolveClass(c);        }        return c;    }}

通过对比我们发现方法1调用了方法2,并且调用方法2的过程中,resolve参数的值为false。javadoc中关于该方法的resolve参数的说明如下:

@param  resolve If <tt>true</tt> then resolve the class参数resolve 如果值为true则resolve这个类

我们看一下resolvetrue时,方法的执行逻辑

if (resolve) {    resolveClass(c);}

在看一下resolveClass方法

    /**     * Links the specified class.  This (misleadingly named) method may be     * used by a class loader to link a class.  If the class <tt>c</tt> has     * already been linked, then this method simply returns. Otherwise, the     * class is linked as described in the "Execution" chapter of     * <cite>The Java&trade; Language Specification</cite>.     * </p>     *     * @param  c     *         The class to link     *     * @throws  NullPointerException     *          If <tt>c</tt> is <tt>null</tt>.     *     * @see  #defineClass(String, byte[], int, int)     */    protected final void resolveClass(Class<?> c) {        resolveClass0(c);    }

从javadoc中我们可以看出,resolveClass方法主要是用来链接指定的类,通过resolve参数我们可以发现,该参数控制了类加载过程的第二步(链接),该参数值为false时不进行类的链接,为true时进行类的链接,由于loadClass(String name, boolean resolve)为protected方法,因此我们无法通过ClassLoader直接调用。
示例代码:

package com.ips.classloader;public class ClassLoaderDemo1 {    public static void main(String [] args){        try {            ClassLoader system = ClassLoader.getSystemClassLoader();            Class<Config> cls = null;            System.out.println("-----方法4-----");            cls = (Class<Config>)ClassLoader.getSystemClassLoader().loadClass("com.ips.classloader.Config");        } catch (Exception e) {            e.printStackTrace();        }    }}

执行结果:

-----方法4-----

没有执行Config类的静态代码块,由此可见Config只是进行了装载,没有进行链接与初始化。

3、关于mysql jdbc

我们在进行数据库操作的时候,通常采用如下的方式加载数据库驱动。

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

为什么不是ClassLoader.getSystemClassLoader().loadClass()呢?这是因为Driver类中的静态代码块需要进行一些初始化配置。代码如下:

 Copyright  2002-2004 MySQL AB, 2008 Sun Microsystemspackage com.mysql.jdbc;import java.sql.SQLException;/** * The Java SQL framework allows for multiple database drivers. Each driver * should supply a class that implements the Driver interface *  * <p> * The DriverManager will try to load as many drivers as it can find and then * for any given connection request, it will ask each driver in turn to try to * connect to the target URL. *  * <p> * It is strongly recommended that each Driver class should be small and * standalone so that the Driver class can be loaded and queried without * bringing in vast quantities of supporting code. *  * <p> * When a Driver class is loaded, it should create an instance of itself and * register it with the DriverManager. This means that a user can load and * register a driver by doing Class.forName("foo.bah.Driver") *  * @see org.gjt.mm.mysql.Connection * @see java.sql.Driver * @author Mark Matthews * @version $Id$ */public class Driver extends NonRegisteringDriver implements java.sql.Driver {    // ~ Static fields/initializers    // ---------------------------------------------    //    // Register ourselves with the DriverManager    //    static {        try {            java.sql.DriverManager.registerDriver(new Driver());        } catch (SQLException E) {            throw new RuntimeException("Can't register driver!");        }    }    // ~ Constructors    // -----------------------------------------------------------    /**     * Construct a new driver and register it with DriverManager     *      * @throws SQLException     *             if a database error occurs.     */    public Driver() throws SQLException {        // Required for Class.forName().newInstance()    }}

4、总结

  • Class.forName() 方法中,initialize参数控制类在加载的过程中是否进行初始化。
  • ClassLoader.getSystemClassLoader().loadClass()方法中,resolve参数控制类在加载的过程中是否进行链接。
1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 荣耀v10锁屏密码忘了怎么办 荣耀9锁屏密码忘记了怎么办 华为荣耀v9锁屏密码忘记了怎么办 荣耀手机密码忘了怎么办数字锁 华为畅享5忘了开机密码怎么办 华为p9连接热点忘了密码怎么办 苹果手机用联通卡信号不好怎么办 魅族手机充电口松了怎么办 华为手机刷机失败开不了机怎么办 华为刷机失败开不了机怎么办 银行卡信息被盗密码被改该怎么办 全民k歌手机话筒有杂音怎么办 手机刷机清除数据需要密码怎么办 oppo手机屏锁密码忘了怎么办 华为手机摔了一下开不了机怎么办 华为v9手机删除隐私空间了怎么办 华为荣耀畅玩5x卡顿怎么办 淘宝买家收到货后恶意退款怎么办 手机淘宝申请退款后不想退了怎么办 买房交首付时的收据发票掉了怎么办 苹果商城消费提示问题忘记了怎么办 psd文件超过2g不能存储怎么办 手机拍的照片做微信头像太大怎么办 上传的照片在等待中传不上去怎么办 淘宝购物车里的图片模糊怎么办 天猫超市一箱饮料少两瓶怎么办 网上卖一件代发顾客要退货怎么办 京东买东西卖家拒绝发货怎么办 淘宝不小心退款给买家了怎么办 不小心智能清理了淘宝物流怎么办 移动卡绑定了太多东西换联通怎么办 淘宝上卖出的东西快递弄丢了怎么办 京东第三方不确认收货怎么办? 天猫评价被判定为广告怎么办 天猫一个订单用卷分单退货怎么办 天猫对已付款成功后自动退款怎么办 拼多多新人红包减价卖家怎么办 魔力宝贝手机版注册人数已满怎么办 买家投诉虚假签收淘宝卖家该怎么办 手机淘宝商家老打骚扰电话怎么办 手机上查询详单忘记服务密码怎么办