Java类加载一:Class.forName()和ClassLoader.getSystemClassLoader().loadClass()区别
来源:互联网 发布:反馈神经网络算法 c 编辑:程序博客网 时间:2024/06/05 03:42
要想搞清楚这两个方法的区别,我们需要了解一下Class的加载过程。Class的加载过程分为三步:
- 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
@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
参数默认为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) //方法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这个类
我们看一下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 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
参数控制类在加载的过程中是否进行链接。
- Java类加载一:Class.forName()和ClassLoader.getSystemClassLoader().loadClass()区别
- Java类加载一:Class.forName()和ClassLoader.getSystemClassLoader().loadClass()区别
- Class.forName()和ClassLoader.getSystemClassLoader().loadClass()区别
- Java中Class.forName()和ClassLoader.loadClass()的区别
- Java中Class.forName和ClassLoader.loadClass的区别
- Java中Class.forName和ClassLoader.loadClass的区别
- Java中Class.forName和ClassLoader.loadClass的区别
- Class.forname() 和 ClassLoader.loadClass()的区别
- Class.forName() 和 ClassLoader.loadClass()的区别?
- Class.forName() 和 ClassLoader.loadClass()的区别?
- Class.forName() 和 ClassLoader.loadClass()的区别
- Class.forName() 和 ClassLoader.loadClass()的区别?
- Class.forName() 和 ClassLoader.loadClass()的区别?
- Class.forName和ClassLoader.loadClass区别
- Class.forName和ClassLoader.loadClass区别
- Class.forName() 和 ClassLoader.loadClass()的区别
- Class.forName和ClassLoader.loadClass区别
- Class.forName() 和 ClassLoader.loadClass()的区别?
- POJ 1061 扩展欧几里得
- 用Canvas模拟苹果画图工具
- eui基本知识点
- day18FileReader读取一种read方法和字符串读取,fileWriter的写入write方法,时间的类calendar
- 关于STL中map的随笔
- Java类加载一:Class.forName()和ClassLoader.getSystemClassLoader().loadClass()区别
- Android Studio快速获取数字签名SHA1
- shader学习笔记 - 水底效果
- SDUT 3661 MoutainTop (单调栈) -- 解题报告
- 小白的成长之路吧。。血泪史啊。
- 取iphone本机ip地址方法(非调用私有api方法)
- 环境配置_Tomcat
- 03-VTK基础概念(2) - 博客频道
- 【react-native-0.31-iOS】添加热更新功能-iOS(04)