ClassLoader知识
来源:互联网 发布:淘宝网手提电脑 编辑:程序博客网 时间:2024/05/16 06:40
(1)ClassLoader基本概念
与C或C++编写的程序不同,Java程序并不是一可执行文件,而是由许多独立的类文件组成的,每个文件对应一个Java类。此外,这些类文件并非全部装入内存,而是根据程序需要逐渐载入。ClassLoader是JVM实现的一部分,ClassLoader包括bootstrap classloader(启动类加载器),ClassLoader在JVM运行的时候加载Java核心的API,以满足Java程序最基本的需求,其中就包括用户定义的ClassLoader,这里所谓的用户定义,是指通过Java程序实现的两个ClassLoader:一个是ExtClassLoader(称为扩展类加载器,负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的所有jar),它的作用是用来加载Java的扩展API,也就是/lib/ext中的类;第二个是AppClassLoader(称为系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件),它是用来加载用户机器上CLASSPATH设置目录中的Class的,通常在没有指定ClassLoader的情况下,程序员自定义的类就由该ClassLoader加载。
注意: 除了Java默认提供的三个ClassLoader之外,用户还可以根据需要定义自已的ClassLoader,而这些自定义的ClassLoader都必须继承自java.lang.ClassLoader类,也包括Java提供的另外二个ClassLoader(Extension ClassLoader和App ClassLoader)在内,但是Bootstrap ClassLoader不继承自ClassLoader,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。
(2)ClassLoader的原理介绍
ClassLoader层次结构:
UML类图:
下面看一下ClassLoader中的一端源码
从上面的源码可以看出,ClassLoader使用的是双亲委托模型来搜索类的,每个ClassLoader实例都有一个父类加载器的引用(不是继承的关系,是一个包含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但可以用作其它ClassLoader实例的的父类加载器。当一个ClassLoader实例需要加载某个类时,它会试图亲自搜索某个类之前,先把这个任务委托给它的父类加载器,这个过程是由上至下依次检查的,首先由最顶层的类加载器Bootstrap ClassLoader试图加载,如果没加载到,则把任务转交给Extension ClassLoader试图加载,如果也没加载到,则转交给App ClassLoader 进行加载,如果它也没有加载得到的话,则返回给委托的发起者,由它到指定的文件系统或网络等URL中加载该类。如果它们都没有加载到这个类时,则抛出ClassNotFoundException异常。否则将这个找到的类生成一个类的定义,并将它加载到内存当中,最后返回这个类在内存中的Class实例对象。
(3)为什么要使用这种父类委托模式呢
因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义的类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时就被引导类加载器(Bootstrcp ClassLoader)加载,所以用户自定义的ClassLoader永远也无法加载一个自己写的String,除非你改变JDK中ClassLoader搜索类的默认算法。
(4)ClassLoader加载流程
当运行一个程序的时候,JVM启动,运行bootstrap classloader,该ClassLoader加载Java核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程。
- 调用
findLoadedClass(String)
来检查是否已经加载类。 - 在父类加载器上调用
loadClass
方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。 - 调用
findClass(String)
方法查找类,这个方法在ClassLoader中并未实现,由其子类负责实现。findClass()的功能是找到class文件并把字节码加载到内存中。
(5)一些重要的方法
a、loadClass方法
ClassLoader.loadClass()是ClassLoader的入口点。该方法的定义如下:
Class loadClass(String name,boolean resole);
name是指JVM需要的类的名称,如Foo或java.lang.Object。resolve参数告诉方法是否需要解析类。在准备执行之前,应考虑类解析。注意:并不总是需要解析,如果JVM只需要知道该类是否存在或找出该类额超类,那么就不需要解析。
b、defineClass方法
defineClass方法接受由原始字节组成的数组,并把它转换成Class对象。原始数组包含如从文件系统或网络装入的数据。defineClass管理JVM的许多复杂的实现层面——它把字节码分析成运行时数据结构、校验有效性等。因为defineClass方法被标记成final的,所以也不能覆盖它。
c、findSystemClass方法
findSystemClass方法从本地文件系统装入文件。它在本地文件系统中寻找类文件,如果存在,就使用defineClass将原始字节转换成Class对象,以将该文件转换成类。当运行Java应用程序时,这是JVM正常装入类的默认机制。对于定制的ClassLoader,只有在尝试其他方法装入之后,再使用findSystemClass。这是因为ClassLoader是负责执行装入类的相关步骤,不负责所有类的所有信息。例如,即使ClassLoader从远程的Web站点装入了某些类,仍然需要在本地机器上装入大量的基本Java库。而这些类库不是我们所关心的,所以要JVM以默认方式从本地文件系统装入它们,这就是findSystemClass的用途。
d、resolveClass方法
正如前面提到的,可以不完全(不带解析)装入类,也可以完全地装入类。当编写我们自己的loadClass时,可以调用resolveClass,这取决于loadClass的resolve参数的值。
e、findLoadedClass方法
findLoadClass充当一个缓存:当请求装入类时,它调用该方法来查看ClassLoader是否已装入这个类,这样可以避免重新装入已存在的类所造成的麻烦。
f、findClass方法
loadClass默认实现调用这个新方法。findClass的用途包含ClassLoader的所有特殊代码,而无须复制器代码(例如,当专门的方法失败时,调用系统ClassLoader)。
目的是从本地文件系统使用实现的类加载器装载一个类。为了创建自己的类装载器,应该扩展ClassLoader类,这是一个抽象类。可以创建一个FileClassLoaderextends ClassLoader,然后覆盖ClassLoader中的findClass(String name)方法,这个方法通过类的名字得到一个Class对象。
public Class findClass(String name)
{
byte [] data=loadClassData(name);
return defineClass(name,data,0,data.length);
}
g、getSystemClassLoader方法
如果覆盖findClass或loadClass,getSystemClassLoader能以实际的ClassLoader对象来访问系统ClassLoader(而不是固定地从findSystemClass调用它)。为了将类请求委托给父类ClassLoader,这个新方法允许ClassLoader获取它的父类ClassLoader.当使用特殊方法是,定制的ClassLoader不能找到类时,可以使用这种方法。
父类ClassLoader被定义成创建该ClassLoder所包含代码的 对象的ClassLoader.
h、forName方法
Class类中有一个静态方法forName,这个方法和ClassLoader中的loadClass方法的目的一样,都是用来加载class的,但是两者在作用上却有区别。
关于forName()方法
这个方法总是返回要加载的类的Class类的实例
1、forName(String className)单参数时, initialize=true
a.总是使用当前类装载器(也就是装载执行forName()请求的类 的类装载器)
b.总是初始化这个被装载的类(当然也包括:装载、连接、初始化)
2、forName(String className, boolean initialize, ClassLoader loader)
a.loader指定装载参数类所用的类装载器,如果null则用bootstrp装载器。
b.initialize=true时,肯定连接,而且初始化了;
c.false时,绝对不会初始化,但是可能被连接了,但是这里有个例外,如果在调用这个forName()前,已经被初始化了(当然,这里也暗含着:className是被同一个loader所装载的,即被参数中的loader所装载的,而且这个类被初始化了),那么返回的类型也肯定是被初始化的
关于用户自定义的类装载器的loadClass()方法
1、loadClass(String name)单参数时, resolve=false
a.如果这个类已经被这个类装载器所装载,那么,返回这个已经被装载的类型的Class的实例,否则,就用这个自定义的类装载器来装载这个class,这时不知道是否被连接。绝对不会被初始化
b.这时唯一可以保证的是,这个类被装载了。但是不知道这个类是不是被连接和初始化了
2、loadClass(String name, boolean resolve)
a.resolve=true时,则保证已经装载,而且已经连接了。resolve=falses时,则仅仅是去装载这个类,不关心是否连接了,所以此时可能被连接了,也可能没有被连接。
另外:这里所谓的“初始化”是指类的初始化,即执行了className字节码的<clinit>方法
再者:类是这样加载的
1、装载
2、连接
a)验证-->检查类格式等
b)准备-->给类变量分配内存,并根据类变量类型设置默认值(即内存中置0)
c)解析-->常量池解析
3、初始化
即执行Java代码的字节码的<clinit>方法,给类变量赋予程序员需要的值
import java.net.URL;public class Sample { public static void main(String[] args) { ClassLoader cloader; cloader = ClassLoader.getSystemClassLoader(); System.out.println(cloader); while (cloader != null ) { cloader = cloader.getParent(); System.out.println(cloader); } try { Class<Object> c1 = (Class<Object>)Class.forName("java.lang.Object" ); cloader = c1.getClassLoader(); System.out.println( "java.lang.Object's loader is " + cloader); Class<Sample> c2 = (Class<Sample>) Class.forName("Sample" ); cloader = c2.getClassLoader(); System.out.println( " Sample's loader is " + cloader); } catch (Exception e) { e.printStackTrace(); } }}
参考来源:http://www.cnblogs.com/dragonsuc/p/4378877.html
http://blog.csdn.net/iceman1952/article/details/1523025
http://blog.csdn.net/xyang81/article/details/7292380
http://blog.csdn.net/vking_wang/article/details/17162327
- ClassLoader知识
- ClassLoader知识
- classloader预备知识
- ClassLoader背景知识
- 深入JVM:ClassLoader相关知识简介
- Java虚拟机ClassLoader知识详解(1)
- Java虚拟机ClassLoader知识详解(2)
- 知识总结之 插件化基础ClassLoader
- classLoader
- classloader
- ClassLoader
- ClassLoader
- ClassLoader
- classloader
- ClassLoader
- ClassLoader
- classloader
- ClassLoader
- Hibernate之jpa实体映射的三种继承关系
- 递归和迭代
- WinSCP复制粘贴时出现文件夹名重复异常
- QtConcurrent简单示例
- 添加依赖后可能出现的各种问题
- ClassLoader知识
- MPAndroidChart
- 本次框架搭建bugs
- jdk1.5的特性
- 我的H5生涯3
- ftp连接服务器失败:响应:220-FileZilla Server version 0.9.24 beta 响应:220-written by Tim Kosse (Tim.Kosse@gmx.d
- javah提示找不到类文件
- mysql 外键的作用
- Max Consecutive Ones