类加载器的层次结构:

引导类加载器(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. SecureCRT中文显示乱码

    环境:SecureCRT登陆REDHAT5.3 LINUX系统 问题:vi编辑器编辑文件时文件中的内容中文显示乱码,但是直接使用linux系统terminal打开此文件时中文显示正常,确诊问题出现在客 ...

  2. 【转】saiku与kylin整合备忘录

    http://blog.csdn.net/freefishly/article/details/51759133 为什么要整合? Kylin是通过离线预计算将Hive中事实表的各组合维度的值存储在Hb ...

  3. Loadrunner中动态添加虚拟用户

    添加的虚拟用户会立马开始执行: 场景组的两种模式: Vuser组模式: Vuser组模式中,对于压力负载机是不能同时添加多个 但是我们可以在一个脚本里通过多次添加Vuser,每次添加30个Vuser, ...

  4. JS获得鼠标位置

    <body> <script> function mouseMove(ev) { ev = ev || window.event; var mousePos = mouseCo ...

  5. https://www.nginx.com/blog/introduction-to-microservices/

    https://www.nginx.com/blog/introduction-to-microservices/

  6. bootstrap datetimepicker 中只显示年或者只显示月份

    1.只显示datetimepicker  日历中只显示年份 $("#day-access-calendar").datepicker({ startView: 2, maxView ...

  7. MySql 查询数据库中所有表名

    查询数据库中所有表名select table_name from information_schema.tables where table_schema='csdb' and table_type= ...

  8. Golang 安装及配置教程 for Mac

    1.到golang.org下载golang 并安装. 2.安装sublimetext ,打开之后 按ctrl+` 打开命令行,输入以下内容: import urllib2,os; pf='Packag ...

  9. Hadoop YARN资源隔离技术

    YARN对内存资源和CPU资源采用了不同的资源隔离方案.对于内存资源,它是一种限制性资源,它的量的大小直接决定应用程序的死活,因为应用程序到达内存限制,会发生OOM,就会被杀死.CPU资源一般用Cgr ...

  10. POJ3469 Dual Core CPU(最小割)

    形象生动的最小割.. #include<cstdio> #include<cstring> #include<queue> #include<algorith ...