Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论

创建用户自定义的类加载器

  要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定的类的名字,返回对应的Class对象的引用。

自定义类加载器的例子

  代码:

package com.mengdd.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream; public class MyClassLoader extends ClassLoader { private String name; // 类加载器的名字
private String path = "d:\\"; // 加载类的路径
private final String fileType = ".class"; // class文件的扩展名 public MyClassLoader(String name) {
super(); // 让系统类加载器成为该类加载器的父加载器
this.name = name;
} public MyClassLoader(ClassLoader parent, String name) {
super(parent); // 显式指定该类加载器的父加载器
this.name = name;
} @Override
public String toString() {
return this.name;
} public String getPath() {
return path;
} public void setPath(String path) {
this.path = path;
} @Override
public Class<?> findClass(String name) throws ClassNotFoundException {
// 重写的时候把protected改为public // 获取字节数组
byte[] data = this.loadClassData(name);
// 将字节数组转换成Class对象返回
return this.defineClass(name, data, 0, data.length); } /**
* 得到class文件的二进制字节数组
*
* @param name
* @return
*/
private byte[] loadClassData(String name) { InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null; try { // 将完整类名中的.转化成\
name = name.replace(".", "\\");
is = new FileInputStream(new File(path + name + fileType)); baos = new ByteArrayOutputStream();
int ch = 0;
while (-1 != (ch = is.read())) { baos.write(ch);
} data = baos.toByteArray();
}
catch (Exception e) {
e.printStackTrace();
}
finally {
try {
is.close();
baos.close();
}
catch (Exception e2) {
}
}
return data;
} // main方法用来测试
public static void main(String[] args) throws Exception { MyClassLoader loader1 = new MyClassLoader("loader1");
// loader1的父加载器是系统类加载器
// 系统类加载器会在classpath指定的目录中加载类
loader1.setPath("d:\\myapp\\serverlib\\"); MyClassLoader loader2 = new MyClassLoader(loader1, "loader2");
// 将loader1作为loader2的父加载器
loader2.setPath("d:\\myapp\\clientlib\\"); MyClassLoader loader3 = new MyClassLoader(null, "loader3");
// loader3的父加载器是根类加载器
loader3.setPath("d:\\myapp\\otherlib\\"); // 测试加载
test(loader2);
test(loader3);
System.out.println("test2---------------");
// 测试不同命名空间的类的互相访问
test2(loader3);
} public static void test(ClassLoader loader) throws Exception {
Class clazz = loader.loadClass("com.mengdd.classloader.Sample"); Object object = clazz.newInstance(); } public static void test2(ClassLoader loader) throws Exception {
Class clazz = loader.loadClass("com.mengdd.classloader.Sample"); Sample object = (Sample) clazz.newInstance(); System.out.println("sample v1: " + object.v1); }
}

  其中Sample:

package com.mengdd.classloader;

public class Sample {

    public int v1 = 1;

    public Sample() {
System.out.println("Sample is loaded by: "
+ this.getClass().getClassLoader()); // 主动使用Dog类
new Dog();
}
}

  Dog类:

package com.mengdd.classloader;

public class Dog {

    public Dog() {
System.out.println("Dog is loaded by: "
+ this.getClass().getClassLoader());
}
}

  例子演示过程略,尝试把class文件放在不同的路径下,看输出或者报错结果。

  主要结论就是验证了父亲委托机制。

  采用loader1的时候由于其父类是系统类加载器(也即应用类加载器),所以如果可以在classpath中找到目标.class文件,则定义类加载器是系统类加载器,输出类似:

  sun.misc.Launcher$AppClassLoader@7448bc3d

  每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成。

  在Sample类中主动使用了Dog类,当执行Sample类的构造方法中的new Dog()语句时,Java虚拟机需要先加载Dog类,到底用哪个类加载器加载呢?

  从打印结果可以看出,Java虚拟机会用Sample类的定义类加载器去加载Dog类,加载过程也同样采用父亲委托机制

  如果Sample类首次主动使用Dog时,Sample类的加载器及它的父加载器都无法加载Dog类,将会抛出找不到文件的异常。

不同类加载器的命名空间关系

  同一个命名空间内的类是相互可见的,即可以互相访问。

  子加载器的命名空间包含所有父加载器的命名空间。

  因此由子加载器加载的类能看见父加载器加载的类。

  例如系统类加载器加载的类能看见根类加载器加载的类。

  由父加载器加载的类不能看见子加载器加载的类。

  可以理解为:由于子加载器中含有父加载器的引用,所以子加载器的范围更大

 

  如果两个加载器之间没有直接或间接的父子关系,那么它们各自加载的类相互不可见。

  比如这么一种情况:MyClassLoader类由系统类加载器加载,而Sample类由loader3类加载器加载,因此MyClassLoader类看不见Sample类。

  在MyClassLoader类的main()方法中使用Sample类,会导致错误。

  当两个不同命名空间内的类互相不可见时,可采用Java反射机制来访问对方实例的属性和方法,即反射可以突破命名空间的限制。

参考资料

  圣思园张龙老师Java SE视频教程。

  ClassLoader类:http://docs.oracle.com/javase/7/docs/api/

  相关博文:

  Java虚拟机JVM学习05 类加载器的父委托机制:http://www.cnblogs.com/mengdd/p/3562540.html

Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论的更多相关文章

  1. Java虚拟机JVM学习05 类加载器的父委托机制

    Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...

  2. Java虚拟机JVM学习07 类的卸载机制

    Java虚拟机JVM学习07 类的卸载机制 类的生命周期 当Sample类被加载.连接和初始化后,它的生命周期就开始了. 当代表Sample类的Class对象不再被引用,即不可触及时,Class对象就 ...

  3. Java虚拟机JVM学习02 类的加载概述

    Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...

  4. Java虚拟机JVM学习01 流程概述

    Java虚拟机JVM学习01 流程概述 Java虚拟机与程序的生命周期 一个运行时的Java虚拟机(JVM)负责运行一个Java程序. 当启动一个Java程序时,一个虚拟机实例诞生:当程序关闭退出,这 ...

  5. 【JVM学习笔记】类加载器

    概述 类加载器用来把类加载到Java虚拟机中.从JDK1.2版本开始,类的加载过程采用父委托机制,这种机制能更好地保证Java平台的安全.在此委托机制中,除了Java虚拟机自带的根类加载器以外,其余的 ...

  6. java之jvm学习笔记四(安全管理器)

    java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...

  7. Java虚拟机JVM学习04 类的初始化

    Java虚拟机JVM学习04 类的初始化 类的初始化 在初始化阶段,Java虚拟机执行类的初始化语句,为类的静态变量赋予初始值. 在程序中,静态变量的初始化有两种途径: 1.在静态变量的声明处进行初始 ...

  8. Java虚拟机JVM学习03 连接过程:验证、准备、解析

    Java虚拟机JVM学习03 连接过程:验证.准备.解析 类被加载后,就进入连接阶段. 连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去. 连接阶段三个步骤:验证.准备和解析. 类 ...

  9. java类加载器学习2——自定义类加载器和父类委托机制带来的问题

    一.自定义类加载器的一般步骤 Java的类加载器自从JDK1.2开始便引入了一条机制叫做父类委托机制.一个类需要被加载的时候,JVM先会调用他的父类加载器进行加载,父类调用父类的父类,一直到顶级类加载 ...

随机推荐

  1. 创建支持多种屏幕尺寸的Android应用

    Android涉及各种各样的支持不同屏幕尺寸和密度的设备.对于应用程序,Android系统通过设备和句柄提供了统一的开发环境,大部分工作是校正每一个应用程序的用户界面到它显示的屏上.与此同时,系统提供 ...

  2. postgres中的中文分词zhparser

    postgres中的中文分词zhparser postgres中的中文分词方法 基本查了下网络,postgres的中文分词大概有两种方法: Bamboo zhparser 其中的Bamboo安装和使用 ...

  3. CSS魔法堂:再次认识font

    一.前言 文字承载着站点内涵,而良好的字体.排版则为用户提供舒适的阅读体验.本文打算对字体稍微深入一下子网页字体的内容,若有纰漏请大家指正,谢谢! 目录一坨: 二, 字体分类 1. 衬线体(Serif ...

  4. 简单认识C#

    C#浅解众所周知c#是微软推出的一款完全没面向对象的编程语言,那么对象是什么?在现实生活中人们一提到对象首先想到的就是“情侣”!但是在我们的程序中对象是什么? 在程序中个能够区别于其他事物的独立个体我 ...

  5. 【转载】HttpWebRequest开启gzip压缩简介

    在用HttpWebRequest对象时,一般我们都没有开启gzip压缩,如果服务端返回的数据比较大,这是我们需要开启gzip压缩,怎么开启呢? 1.给HttpWebRequest对象,添加如下Head ...

  6. LINQ to SQL语句(5)之Order By

    适用场景:对查询出的语句进行排序,比如按时间排序等等. 说明:按指定表达式对集合排序:延迟,:按指定表达式对集合排序:延迟,默认是升序,加上descending表示降序,对应的扩展方法是OrderBy ...

  7. BUG集锦

    1.0 jQuery 序列化表单数据 serialize() 得到的是 ""; 表单元素没有name属性.(用的美工的页面,没注意这个细节.)

  8. 一篇文看懂Hadoop:风雨十年,未来何去何从

    本文分为技术篇.产业篇.应用篇.展望篇四部分 技术篇 2006年项目成立的一开始,“Hadoop”这个单词只代表了两个组件——HDFS和MapReduce.到现在的10个年头,这个单词代表的是“核心” ...

  9. Rest风格中关于JPA使用懒加载的坑

    公司最近使用的ORM框架是JPA实现产品使用的是hibernate,曾经看过一篇博客上面说的是如果团队里面没有一个精通hibernate的人,那么最好不要使用它,我现在是深刻的体会到了.但是使用什么框 ...

  10. InfluxDB学习之InfluxDB的基本操作

    InfluxDB提供类SQL语法,如果熟悉SQL的话会非常容易上手.本文就为大家介绍一下InfluxDB的基本操作.     InfluxDB提供类SQL语法,如果熟悉SQL的话会非常容易上手. 本文 ...