一、什么是ClassLoader?

  大家都知道,当我们写好一个Java程序之后,不是管是CS还是BS应用,都是由若干个.class文件组织而成的一个完整的Java应用程序,当程序在运行时,即会调用该程序的一个入口函数来调用系统的相关功能,而这些功能都被封装在不同的class文件当中,所以经常要从这个class文件中要调用另外一个class文件中的方法,如果另外一个文件不存在的,则会引发系统异常。而程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存之后,才能被其它class所引用。所以ClassLoader就是用来动态加载class文件到内存当中用的。

二、Java默认提供的三个ClassLoader

  1:BootStrap ClassLoader称为启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等.

  2:Extension ClassLoader:称为扩展类加载器,负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的所有jar。

  3:App ClassLoader:称为系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件。

注意: 除了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类加载器。

三、ClassLoader加载类的原理

1、原理介绍

  ClassLoader使用的是双亲委托模型来搜索类的,每个ClassLoader实例都有一个父类加载器的引用(不是继承的关系,是一个包含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但可以用作其它ClassLoader实例的的父类加载器。当一个ClassLoader实例需要加载某个类时,它会试图亲自搜索某个类之前,先把这个任务委托给它的父类加载器,这个过程是由上至下依次检查的,首先由最顶层的类加载器Bootstrap ClassLoader试图加载,如果没加载到,则把任务转交给Extension ClassLoader试图加载,如果也没加载到,则转交给App ClassLoader 进行加载,如果它也没有加载得到的话,则返回给委托的发起者,由它到指定的文件系统或网络等URL中加载该类。如果它们都没有加载到这个类时,则抛出ClassNotFoundException异常。否则将这个找到的类生成一个类的定义,并将它加载到内存当中,最后返回这个类在内存中的Class实例对象。

2、为什么要使用双亲委托这种模型呢?

  因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义的类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时就被引导类加载器(Bootstrcp ClassLoader)加载,所以用户自定义的ClassLoader永远也无法加载一个自己写的String,除非你改变JDK中ClassLoader搜索类的默认算法。

3、 但是JVM在搜索类的时候,又是如何判定两个class是相同的呢?

  JVM在判定两个class是否相同时,不仅要判断两个类名是否相同,而且要判断是否由同一个类加载器实例加载的。只有两者同时满足的情况下,JVM才认为这两个class是相同的。就算两个class是同一份class字节码,如果被两个不同的ClassLoader实例所加载,JVM也会认为它们是两个不同class。比如网络上的一个Java类org.classloader.simple.NetClassLoaderSimple,javac编译之后生成字节码文件NetClassLoaderSimple.class,ClassLoaderA和ClassLoaderB这两个类加载器并读取了NetClassLoaderSimple.class文件,并分别定义出了java.lang.Class实例来表示这个类,对于JVM来说,它们是两个不同的实例对象,但它们确实是同一份字节码文件,如果试图将这个Class实例生成具体的对象进行转换时,就会抛运行时异常java.lang.ClassCaseException,提示这是两个不同的类型。

4、ClassLoader的体系架构:

验证ClassLoader加载类的原理:打印ClassLoader类的层次结构,请看下面这段代码:

 //获得加载ClassLoaderTest.class这个类的类加载器
ClassLoader loader = ClassLoaderTest.class.getClassLoader(); while(loader != null) {
System.out.println(loader);
loader = loader.getParent(); //获得父类加载器的引用
}
System.out.println(loader);

控制台打印出:

sun.misc.Launcher$AppClassLoader@8391b0c
sun.misc.Launcher$ExtClassLoader@5d1eb50b
null

第一行结果说明:ClassLoaderTest的类加载器是AppClassLoader。

第二行结果说明:AppClassLoader的类加器是ExtClassLoader,即parent=ExtClassLoader。

第三行结果说明:ExtClassLoader的类加器是Bootstrap ClassLoader,因为Bootstrap ClassLoader不是一个普通的Java类,所以ExtClassLoader的parent=null,所以第三行的打印结果为null就是这个原因。

四、定义自已的ClassLoader

既然JVM已经提供了默认的类加载器,为什么还要定义自已的类加载器呢?

  因为Java中提供的默认ClassLoader,只加载指定目录下的jar和class,如果我们想加载其它位置的类或jar时,比如:我要加载网络上的一个class文件,通过动态加载到内存之后,要调用这个类中的方法实现我的业务逻辑。在这样的情况下,默认的ClassLoader就不能满足我们的需求了,所以需要定义自己的ClassLoader。

定义自已的类加载器分为两步:

  1、继承java.lang.ClassLoader

  2、重写父类的findClass方法

读者可能在这里有疑问,父类有那么多方法,为什么偏偏只重写findClass方法?

因为JDK已经在loadClass方法中帮我们实现了ClassLoader搜索类的算法,当在loadClass方法中搜索不到类时,loadClass方法就会调用findClass方法来搜索类,所以我们只需重写该方法即可。如没有特殊的要求,一般不建议重写loadClass搜索类的算法。

来源:http://www.importnew.com/15362.html

java lang(ClassLoader)的更多相关文章

  1. 待解决ava.lang.OutOfMemoryError: PermGen space at java.lang.ClassLoader.defineClass1(Native Method)

    java.lang.OutOfMemoryError: PermGen space at java.lang.ClassLoader.defineClass1(Native Method) at ja ...

  2. Caused by: java.lang.ClassNotFoundException: com.opensymphony.xwork2.util.classloader.ReloadingClassLoader

    今天学习到strusts2与spring的整合,把原来可以交给spring产生的东西都扔给了它,终于拜托了繁琐的代码,只专心于逻辑开发就OK了,现在连strusts的action都可以交给spring ...

  3. Thread.currentThread().setContextClassLoader为什么不生效与java.lang.NoClassDefFoundError之Java类加载的Parent first Classloader

    众所周知,Java的类加载机制采用了双亲委派模型,导致在进行类加载的时候会有多个加载器,这种复杂的机制,有时候会导致‘Exception in thread main java.lang.NoClas ...

  4. Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory

    学习架构探险,从零开始写Java Web框架时,在学习到springAOP时遇到一个异常: "C:\Program Files\Java\jdk1.7.0_40\bin\java" ...

  5. myeclipse 无法启动 java.lang.IllegalStateException: Unable to acquire application service. Ensure that the org.eclipse.core.runtime bundle is resolved and started (see config.ini).

    把myeclipse10 按照目录完整拷贝到了另外一台电脑, 另外的目录 原安装目录 D\:\soft\i\myeclipse10 新安装目录 E\:\soft\myeclipse10 双击启动失败, ...

  6. 执行打的maven jar包时出现“Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes”

    Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for ...

  7. 【java.lang.UnsupportedClassVersionError】版本不一致出错

    这种错误的全部报错信息: java.lang.UnsupportedClassVersionError: org/apache/lucene/store/Directory : Unsupported ...

  8. 【异常】Caused by: java.lang.ClassNotFoundException: org.springframework.dao.DataIntegrityViolationException

    Caused by: java.lang.ClassNotFoundException: org.springframework.dao.DataIntegrityViolationException ...

  9. 在写junit test 的时候出现的java.lang.UnsupportedClassVersionError问题

    今天在写为一些project 写 一些junit  test  的时候,出现下面的异常: java.lang.UnsupportedClassVersionError: moneytest/Money ...

随机推荐

  1. 分布式UUID的生成

    背景 最近有个项目:涉及到分布式计算,tps相对较高,流程之间是异步调用,流程间相互依赖的对象(涉及记录外键)需要持久化.这就衍生出了需要在JVM中快速生成分布式UUID的问题 方案 1.通过JDK标 ...

  2. 【Linux基础】history查看历史命令

    1.history命令 “history”命令就是历史记录.它显示了在终端中所执行过的所有命令的历史. history //显示终端执行过的命令 history //显示最近10条终端执行过的命令 C ...

  3. localhost和127.0.0.1及ip区别

    1.127.0.0.1是回送地址,指本地机,一般用来测试使用.回送地址是本机回送地址(Loopback Address),即主机IP堆栈内部的IP地址,主要用于网络软件测试以及本地机进程间通信,无论什 ...

  4. pom文件miss artifact com.sun:tools:jar:1.5.0:system问题

    问题现象: 导入新的maven项目时,有时候pom.xml文件会提示一个错误信息:Missing artifact com.sun:tools:jar:1.5.0:system 问题原因: maven ...

  5. Yahoo团队网站性能优化的35条黄金守则

    转载 Excetional Performance 团队总结出了一系列可以提高网站速度的方法.可以分为 7大类 35条.包括内容 .服务器 . CSS . JavaScript .Cookie .图片 ...

  6. CPU, PSU, SPU - Oracle Critical Patch Update Terminology Update

    It all started in January 2005 with Critical Patch Updates (CPU).  Then Patch Set Updates (PSU) were ...

  7. Python:Day06 元组、字典、字符串

    tuple(元组) 元组被称为只读列表,即数据可以被查询,但不能被修改,所以列表的切片操作同样适用于元组. 元组写在( )里,元素之间用逗号隔开. tul1 = ( ) #空元组 tul2 = (20 ...

  8. 网站建设部署与发布--笔记2-部署Apache

    网站部署(Linux) 部署Apache 操作系统:CentOS 7.2 1.首先连接云服务器,清楚系统垃圾. $ yum clean all Loaded plugins: fastestmirro ...

  9. ubantu服务器配置ss

    阿里云 ubantu16.0(自带pip) 服务端 $ apt-get install python-pip $ pip install shadowsocks $ vim /etc/shadowso ...

  10. git&github入门使用

    一.在Linux上安装Git 首先,你可以试着输入git,看看系统有没有安装Git,没有就yum一个,反正也是自己玩玩 二.版本库创建 什么是版本库呢?版本库又名仓库,英文名repository,你可 ...