一,ClassLoader 概念
Java中实例化一个类等操作之前需要把类装入虚拟机中。这个国政就是类加载,即加载,连接,初始化。其中的加载过程就是由类加载器 ClassLoader 完成的。
基本上所有的类加载器都是java.lang.ClassLoader 类的一个实例。
二,ClassLoader分类
Java中的类加载器可以分成两类
- 系统提供的
- 开发人员自己编写
系统提供的类加载器主要由三个:
- 启动类加载器(Bootstrap ClassLoader)
- 扩展类加载器(Extensions ClassLoader),负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的所有jar
- 系统类加载器(System ClassLoader),负责加载应用程序classpath目录下的所有jar和class文件。
盗用这里的各种类加载器之间的关系图:
除了系统提供的类加载器之外,开发人员可以通过继承java.lang.ClassLoader类来自己实现类加载器。
上图可以看到除了启动类加载器其他加载器都有自己的父类。
三,双亲委派机制
如何判定两个Java类相同
Java 虚拟机判定两个类是否相同,不仅要看类的全名,还要看加载此类的类加载器是否相同。只有两者都相同才会认为相同。
如果试图在两个类对象之间进行赋值操作,会抛出java.lang.ClassCastException。这个特性为同样名称的Java类在JVM*同存在创建了条件。
什么是双亲委派机制
如果一个类加载器收到了类加载的请求,首先不会自己去尝试加载这个类,而是把请求委派给父类加载器去完成。一次类推,所有的类加载请求都会传递给启动类加载器。父类加载器尝试在对应的类路径下寻找class 字节码文件并载入,如果加载失败,子类才会尝试自己去加载。
双亲委派机制是为了保证Java核心库的类型安全。这种模型保证了Java 类随着它的父类加载器一起具备了一种带优先级的层次关系。例如 java.lang.Object 类,无论哪个类加载器去加载该类,最终都由启动类加载器加载,因此object 类在程序的各种类加载器环境中都是同一个类。否则的话用户自定义一个java.lang.Object 类且放在classpath 中,那么系统中会出现多个Object类,导致程序混乱。
看到ClassLoader源码:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先检查该类是否已经被加载
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
// 如果没被当前装载,则递归的到父中装载
c = parent.loadClass(name, false);
} else {
//装载器到顶部还没找到。则在Bootstrap中找
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;
}
}
loadClass 与 findClass
重写 loadClass 会覆盖掉双亲委派机制,这种情况下就有可能会有大量的相同的类被不同的类加载器加载到内存中。
重写 findClass 依然遵循双亲委派模型
Class.forName
Class.forName是一个静态方法,同样可以用来加载类。该方法有两种形式:Class.forName(String name, boolean initialize, ClassLoader loader)和 Class.forName(String className)。第一种形式的参数 name表示的是类的全名;initialize表示是否初始化类;loader表示加载时使用的类加载器。
第二种形式则相当于设置了参数 initialize的值为 true,loader的值为当前类的类加载器。
Class.forName的一个很常见的用法是在加载数据库驱动的时候。如 Class.forName(“org.apache.derby.jdbc.EmbeddedDriver”).newInstance()用来加载 Apache Derby 数据库的驱动。
参考:
深入分析Java ClassLoader 原理:http://blog.csdn.net/xyang81/article/details/7292380
【深入Java虚拟机】之四:类加载机制:http://blog.csdn.net/ns_code/article/details/17881581
JVM类加载机制:http://blog.sina.com.cn/s/blog_4fe01e630100gu3x.html
深入讨论Java类加载器:https://www.ibm.com/developerworks/cn/java/j-lo-classloader/#ibm-pcon
Java深度历险(二)——Java类的加载、链接和初始化:http://www.infoq.com/cn/articles/cf-Java-class-loader#
类加载、类加载器、双亲委派机制与常见问题:http://blog.csdn.net/vernonzheng/article/details/8461380#comments
http://blog.sina.com.cn/s/blog_69b3e1a901018vdl.html