Java类加载一:Class.forName()和ClassLoader.getSystemClassLoader().loadClass()区别
2016-08-30 18:31
411 查看
要想搞清楚这两个方法的区别,我们需要了解一下Class的加载过程。Class的加载过程分为三步:
loading(装载)
linking(链接)
initializing(初始化)
大家可以通过这篇文章:Java魔法堂:类加载机制入了个门来了解类的详细加载过程。阅读以上文章后,我们一起分析一下两个方法的区别,如有不正之处,欢迎批评指正。
public static Class
public static Class
通过对比我们发现两个方法最终都调用了私有的
通过
等同于
示例代码:
ClassLoaderDemo1代码:
Config代码:
我们通过三种方式加载Config类,在Config类中有一段静态代码块,通过
执行结果如下:
我们发现方法3没有输出flag的值,这是为什么呢?原因是类加载过程中的缓存机制,由于方法1已经加载了该类,因此方法3不会再次加载该类,所以没有输出flag值,为了测试缓存的问题,我们将方法1与方法3的位置互换,程序的执行结果如下,可以看到方法3加载了该类,并且输出去了flag值,而方法1没有输出flag值。我们每次修改完代码都需要重启JVM来执行新的代码也是由类加载的缓存机制造成的。
public Class
protected Class
通过对比我们发现方法1调用了方法2,并且调用方法2的过程中,
我们看一下
在看一下
从javadoc中我们可以看出,
示例代码:
执行结果:
没有执行
为什么不是ClassLoader.getSystemClassLoader().loadClass()呢?这是因为
ClassLoader.getSystemClassLoader().loadClass()方法中,
loading(装载)
linking(链接)
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
@CallerSensitive public 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参数默认为
true,
Class.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) //方法1 protected 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这个类
我们看一下
resolve为
true时,方法的执行逻辑
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™ 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 Microsystems package 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参数控制类在加载的过程中是否进行链接。
相关文章推荐
- Class.forName()和ClassLoader.getSystemClassLoader().loadClass()区别
- Java中class是如何加载到JVM中的(Class.forName("name")和ClassLoader.loadClass("name")的区别)
- Java中class是如何加载到JVM中的(Class.forName(&quot;name&quot;)和ClassLoader.loadClass(&quot;name&quot;)的区别)
- className.class.getResourceAsStream()与ClassLoader.getSystemResourceAsStream() 的区别
- JVM虚拟机加载类到内存的ClassLoader.loadClass && Class.forName()的比较
- Class.forName() 和 ClassLoader.loadClass()的区别?
- Class.forName和ClassLoader.loadClass的区别
- Class.forName() 和 ClassLoader.loadClass()的区别?
- className.class.getResourceAsStream()与ClassLoader.getSystemResourceAsStream() 的区别
- Class.forname() 和 ClassLoader.loadClass()的区别 (转)
- className.class.getResourceAsStream()与ClassLoader.getSystemResourceAsStream() 的区别
- Class.forName() 和 ClassLoader.loadClass()的区别
- className.class.getResourceAsStream()与ClassLoader.getSystemResourceAsStream() 的区别
- Class.forName和ClassLoader.loadClass的比较及类加载原理
- className.class.getResourceAsStream()与ClassLoader.getSystemResourceAsStream() 的区别
- Class.forname() 和 ClassLoader.loadClass()的区别
- Class.forName() 和 ClassLoader.loadClass()的区别?
- java零碎要点---class()、getClass()、Class.forName()、getClassLoader()的区别和联系--又不详细的地方求补充
- Class.forName()与ClassLoader.loadClass()的区别
- Class.forName() 和 ClassLoader.loadClass()的区别?