类加载器的层次结构:

引导类加载器(bootstrap class loader)

  用来加载java的核心库(JAVA_HOME/jre/lib/rt.jar,或sun.boot.class.path路径下的内容),是用原生代码来实现的(C实现的),并不继承自java.lang.ClassLoader。

  加载扩展类和应用程序类加载器,并指定它们的父类加载器。

扩展类加载器(extensions class loader)

  用来加载java的扩展库(JAVA_HOME/jre/lib/ext/*.jar,或java.ext.dirs路径下的内容)java虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载java类。

  有sun.miscLauncher$ExtClassLoader实现,继承自java.lang.ClassLoader

应用程序类加载器(application class loader)

  它根据java应用的类路径(classpath,java.class.path路径)来加载指定路径的类,一般来说,java应用的类都是由它来完成加载的

  由sun.misc.Launcher$AppClassLoader实现,继承自java.lang.ClassLoader

自定义类加载器

  开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。

说明:在java中由于类的加载采用的是双亲委托机制,上面几种类加载器是父子关系,其中引导类加载器为基础。

ClassLoader类介绍

作用:

  java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个java类,即java.lang.Class类的一个实例。

  除此之外,ClassLoader还负责加载java应用所需的资源文件,如图像文件和配置文件等。

相关方法:

getParent()  返回该类加载器的父类加载器

loadClass(String name)  加载名称为name的类,返回的结果是java.lang.Class类的实例

findClass(String name)  查找名称为name的类,返回的结果是java.lang.Class类的实例

findLoadedClass(String name)  查找名称为name的已经被加载过的类,返回的结果是java.lang.Class类的实例

defineClass(String name,byte[] b,int off,int len)  把字节数组b中的内容转换成java类,返回的结果是java.lang.Class类的实例。这个方法被声明为final的。

resolveClass(Class<?> c)  链接指定的java类。

代码测试类加载器:

public class Demo02 {
public static void main(String[] args) {
System.out.println(ClassLoader.getSystemClassLoader());
System.out.println(ClassLoader.getSystemClassLoader().getParent());;
System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());;
}
}

  输出:

sun.misc.Launcher$AppClassLoader@1016632

sun.misc.Launcher$ExtClassLoader@dc6a77

null

依次为应用加载器、扩展加载器和引导加载器(但是引导加载为原生代码所写,因此获取不到,为null)。

类加载器的代理模式:

代理模式:交给其他加载器来加载指定的类。

双亲委托机制:

  就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,以此追溯,直到最高的爷爷辈的,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。

  双亲委托机制是为了保证java核心库的类型安全(这种机制就保证不会出现用户自己能定义java.lang.Object类的情况)。

  类加载器除了用于加载类,也是安全的最基本的屏障。

双亲委托机制是代理模式的一种:

  并不是所有的类加载器都采用双亲委托机制。

  tomcat服务器类加载器也使用代理模式,所不同的是它是首先尝试自己去加载某个类,如果找不到再代理给父类加载器。这与一般类加载器的顺序是相反的。

自定义类加载器的流程:

  继承:java.lang.ClassLoader

  首先检查请求的类型是否已经被这个类装载器装载到命名空间中,如果已经装载,则返回

  委派类将加载请求给父类加载器,如果父类加载器能够完成,则返回父类加载器加载的Class实例

  调用本类加载器的findClass()方法,师徒获取对应的字节码,如果获取得到,则调用defineClass()导入类型到方法区;如果获取不到对应的字节码或者其它原因失败,则返回异常给loadClass(),loadClass()转抛异常,终止加载过程

  注:被两个加载器加载的同一个类,Jvm不认为是相同的类。

示例代码如下:

package com.test;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream; /**
* 自定义文件系统加载器
* @author We.lxk
*
*/
public class FileSystemClassLoader extends ClassLoader{
private String rootDir; public FileSystemClassLoader(String rootDir) {
this.rootDir = rootDir;
} private byte[] getClassData(String classname){ //com.test.User -> rootDir/com/test/User
String path = rootDir +"/"+classname.replace(".", "/")+".class";
//IOUtils 可以使用它将读取的流数据转换为字节数组
InputStream is = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try{
is = new FileInputStream(path); byte[] buffer = new byte[1024];
int temp = 0;
while((temp=is.read(buffer))!=-1){
baos.write(buffer, 0, temp);
}
return baos.toByteArray();
}catch(Exception e){
e.printStackTrace();
return null;
}finally{
try {
if(is!=null)
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if(baos!=null)
baos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} @Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name); //应该先查询有没有加载过这个类。已经加载,则直接返回加载好的类。
if(c!=null){
return c;
}else{
ClassLoader parent = this.getParent();
try{
//System.out.println("hello");
c = parent.loadClass(name); //委派给父类加载
}catch(Exception e){
//e.printStackTrace();
}
if(c!=null){
return c;
}else{
byte[] classData = getClassData(name);
if(classData==null){
throw new ClassNotFoundException();
}else{
c = defineClass(name, classData, 0, classData.length);
}
}
}
return c;
}
}

测试代码:

package com.test;

/**
* 测试自定义的FileSystemClassLoader
* @author We.lxk
*
*/
public class Demo03 {
public static void main(String[] args) throws Exception {
FileSystemClassLoader loader = new FileSystemClassLoader("D:/myJava");
FileSystemClassLoader loader2 = new FileSystemClassLoader("D:/myJava"); Class<?> c = loader.loadClass("com.test.Demos");
Class<?> c2 = loader.loadClass("com.test.Demos");
Class<?> c3 = loader2.loadClass("com.test.Demos"); Class<?> c4 = loader2.loadClass("java.lang.String");
Class<?> c5 = loader.loadClass("com.test.Demo"); System.out.println(c.hashCode()+" "+c.getClassLoader());
System.out.println(c2.hashCode()+" "+c2.getClassLoader());
System.out.println(c3.hashCode()+" "+c3.getClassLoader());
System.out.println(c4.hashCode()+" "+c4.getClassLoader());
System.out.println(c5.hashCode()+" "+c5.getClassLoader());
//System.out.println(.getClassLoader());
}
}

java类加载器的层次结构的更多相关文章

  1. Java类加载器详解

    title: Java类加载器详解date: 2015-10-20 18:16:52tags: JVM--- ## JVM三种类型的类加载器- 我们首先看一下JVM预定义的三种类型类加载器,当一个 J ...

  2. 深入理解Java类加载器(ClassLoader) (转)

    转自: http://blog.csdn.net/javazejian/article/details/73413292 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Ja ...

  3. Java 类加载器解析及常见类加载问题

    Java 类加载器解析及常见类加载问题 java.lang.ClassLoader 每个类加载器本身也是个对象--一个继承 java.lang.ClassLoader 的实例.每个类被其中一个实例加载 ...

  4. 深入理解Java类加载器(二):线程上下文类加载器

    摘要: 博文<深入理解Java类加载器(一):Java类加载原理解析>提到的类加载器的双亲委派模型并不是一个强制性的约束模型,而是Java设计者推荐给开发者的类加载器的实现方式.在Java ...

  5. java笔记--理解java类加载器以及ClassLoader类

    类加载器概述: java类的加载是由虚拟机来完成的,虚拟机把描述类的Class文件加载到内存,并对数据进行校验,解析和初始化,最终形成能被java虚拟机直接使用的java类型,这就是虚拟机的类加载机制 ...

  6. java类加载器深入研究

    看了下面几篇关于类的加载器的文章,豁然开朗.猛击下面的地址开始看吧. Java类加载原理解析      深入探讨 Java 类加载器 分析BootstrapClassLoader/ExtClassLo ...

  7. 深入探讨 Java 类加载器

    转自:http://www.ibm.com/developerworks/cn/java/j-lo-classloader/ 类加载器(class loader)是 Java™中的一个很重要的概念.类 ...

  8. JAVA 类加载器 第14节

    JAVA 类加载器 第14节 今天我们将类加载机制5个阶段中的第一个阶段,加载,又叫做装载.为了阅读好区分,以下都叫做装载. 装载的第一步就是要获得二进制的字节流,它可以从读.class文件获得,也可 ...

  9. 深入探讨 Java 类加载器[转]

    原文地址:http://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html 类加载器(class loader)是 Java™ ...

随机推荐

  1. Liferay 6.2 改造系列之二十一:修改WebSphare下JSONWS服务不生效的BUG

    问题原因是WebSphare下,servletContext.getContextPath()获取到的值为“/”而非空字符串. 在/portal-master/portal-impl/src/com/ ...

  2. NDK各版本下载

    含r8e,r9d,r10c 其中x86_64代表64位系统 官网上只有最新版下载链接,如果想要下载以前的版本,可打开 https://archive.org/web/ 然后输入 http://deve ...

  3. 破解ZIP加密文件密码fcrackzip

    破解ZIP加密文件密码fcrackzip ZIP是最常见的文件压缩方式.由于其压缩算法开源,主流操作系统都支持这种压缩算法.ZIP压缩方式支持密码加密.加密的时候会在文件头部保存密钥相关信息.利用这个 ...

  4. Y86模拟器安装

    Y86模拟器安装 这周需要学习Y86下的指令集开发,Y86和x86可以说是孪生兄弟,但是还是存在着一些小的差别.接下来介绍如何进行linux-debian平台下的Y86模拟器安装. 虚拟机VMware ...

  5. 简单的实现UIpicker上面的取消确定按钮

    1 因为我用的xib实现的添加picker 和textfiled的, @interface ViewController : UIViewController<UITextFieldDelega ...

  6. jQuery回车键提交表单

    $(document).keyup(function(event) {     if(event.keyCode==13)     {         $('btnSubmit').trigger(& ...

  7. linux内存分配

    在linux的内存分配机制中,优先使用物理内存,当物理内存还有空闲时(还够用),不会释放其占用内存,就算占用内存的程序已经被关闭了,该程序所占用的内存用来做缓存使用,对于开启过的程序.或是读取刚存取过 ...

  8. A hard puzzle

    A hard puzzle Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tot ...

  9. BZOJ4209 : 西瓜王

    首先求出区间前$k$大数中奇数的个数和偶数的个数. 如果都是偶数,那么答案就是前$k$大数的和. 否则,要么去掉最小的偶数,加上最大的奇数,要么去掉最小的奇数,加上最大的偶数. 主席树维护即可. 时间 ...

  10. BZOJ3591: 最长上升子序列

    因为是一个排列,所以可以用$n$位二进制数来表示$O(n\log n)$求LIS时的单调栈. 首先通过$O(n^22^n)$的预处理,求出每种LIS状态后面新加一个数之后的状态. 设$f[i][j]$ ...