类加载器的层次结构:

引导类加载器(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. c#基础系列(转)

    转:http://www.cnblogs.com/landeanfen/p/4953025.html C#基础系列——一场风花雪月的邂逅:接口和抽象类 前言:最近一个认识的朋友准备转行做编程,看他自己 ...

  2. 网页打印A4纸-----表格在跨页时自动换页打印的实现 (转)

    在最近所做的一个项目中,需要通过网页来打印不少的表单,但是又不想每个打印页签各占用一个页面,这样就需要生存很多不同的冗余页面,为了减少冗余,所有的表单通过jquery的页签tab来实现的. 一 :基本 ...

  3. JavaScript给onclick事件赋值,动态传参数实例辩错

    错误的例子: <body> <input id="certid" type="text" value="123456" & ...

  4. try catch finally的一些用法

    一:例题: package test; import javax.swing.*; class AboutException { public static void main(String[] a) ...

  5. DNS常用命令

    ipconfig/ifconfig    显示网络信息 nslookup host    查询域名对应的ip同时也显示了网关地址 cat /etc/resolv.conf    查看本地dns服务器地 ...

  6. 《DSP using MATLAB》示例Example4.11

    代码: b = [1, 0]; a = [1, -0.9]; % %% ---------------------------------------------- %% START a determ ...

  7. PHP 下载简历

    下载简历:先生成html模版,然后在下载转化为word格式: 获取数据方法: 先获取数据,然后开启缓存,写入数据,关闭缓存:然后下载成word: /** * 下载简历--简单 * @author La ...

  8. BZOJ2040 : [2009国家集训队]拯救Protoss的故乡

    以根为原点,所有叶子为汇点建立网络. 对于一条边$(x,y,A,B)$,$x$向$y$连边,容量$A$,费用0,再连边,容量$B-A$,费用1. 然后不断增广,直到费用达到$M$为止的最大流即为答案. ...

  9. UVa 11324 & 强联通分量+DP

    题意: 一张无向图,求点集使其中任意两点可到达. SOL: 强联通分量中的点要么不选要么全都选,然后缩点DAG+DP 记录一下思路,不想写了...代码满天飞.

  10. TC SRM 591 DIV2 1000

    很不错的一题,非常巧妙的用DP顺序解决这个问题... 可以发现,只和A里面最小的有关系... #include <cstdio> #include <cstring> #inc ...