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 OpenGL 编写简单滤镜

    Android 上使用Opengl进行滤镜渲染效率较高,比起单纯的使用CPU给用户带来的体验会好很多.滤镜的对象是图片,图片是以Bitmap的形式表示,Opengl不能直接处理Bitmap,在Andr ...

  2. T-SQL中jion操作

    --创建学生表 create table Students( sno nvarchar(10) not null primary key, name nvarchar(30) not null, ge ...

  3. 使用git提交中删除idea

    https://segmentfault.com/q/1010000000720031 http://www.tuicool.com/articles/a6Nf63F 先有项目,然后分享至github ...

  4. WordPress主题模板层次和常用模板函数

    首页: home.php index.php 文章页: single-{post_type}.php – 如果文章类型是videos(即视频),WordPress就会去查找single-videos. ...

  5. QCustomplot使用分享(七) 层(完结)

    一.分层绘制 一直说要讲2.0.0版本,但总是想把1.3.2版本拿出来比较一下,这篇文章也不例外.QCustomPlot2.0.0beta版本比1.3.2release版本有一个很大的改进那就是分层绘 ...

  6. DirectShow .Net 实现视频

    DirectShow .Net 实现视频 .获取视频采集设备IBaseFilter接口对象的方法 //获取所有视频设备名称 public ArrayList GetVideoInputDevice() ...

  7. The Web server is configured to not list the contents of this directory.

    部署一个ASP.NET MVC网站至一个全新的服务器Windows Server 2008 R2, 数据为MS SQL Server 2014 64bit Expression版本. 运行时,它第一次 ...

  8. [水煮 ASP.NET Web API2 方法论](3-9)空气路由的设置

    阅读导航 问题 解决方案 工作原理 代码演示 在此解释一下,空气路由,是本人臆想出来,觉着更能表达 IgnoreRoute 的意图,如果看着辣眼睛^^,请见谅. 问题 我们在之定义过集中式路由,集中式 ...

  9. 使用C#向Sql Sever中存取网络图片和本地图片(二进制流的形式)

    先是做普通的,存储我们本地的图片,将它转化为二进制流存储到数据库对应的表中. 代码如下: string path = "../../A.jpg"; FileStream fs = ...

  10. MEF入门之不求甚解,但力求简单能讲明白(五)

    我们已经成功的达到了目标,大量减少了if else. 不过在园子里面的文章大多,用的是Import.ImportMany.So,修改主函数. using IPart; using System; us ...