- 浏览: 413774 次
- 性别:
- 来自: 济南
文章分类
最新评论
-
pmh905001:
写的很详尽,感谢!
解析jvm.dll和java.exe -
Bll:
插得真深啊,我的是(eclipse_j2ee_juno):F: ...
在eclipse里jsp编译后的java和class文件的位置 -
heming_way:
谢谢,对我很有用,解答了我对多值依赖的疑问
关于多值依赖--范式! -
JavaStudy2011:
java语言解析xml文件 -
vrussell:
Thanks man, it helps me a lot!
获得IEditorPart和IDocument
类装载器是Java程序运行时不可缺少的一部分,它的任务是把由Java源程序编译成的class文件读入到内存中,确切的说是装入到JVM的内存中,应为JVM是一台抽象的计算机,它有着自己的CPU,内存等. 在class文件中包含了一个类的各种信息,当执行java XXX命令运行一个以XXX为初始类的Java程序时,类装载器会把XXX的class文件装载到内存,然后根据该class文件中包含的信息在方法区中生成一个JVM的内部数据结构,该数据结构里的内容是XXX类的类型信息,大家知道每个类都会对应一个Class对象,该对象就是JVM根据这个类型信息在堆区中创建的,这个对象含有一个指向方法区中对应类型信息的指针,除此之外,该Class对象还包含一个指向类装载器的指针,根据这个Class对象就可以创建对象,可以访问装载本类的类装载器,还可以访问方法区中的数据(Java反射机制就是这样实现的).
一.Java中有四中类装载器:启动类装载器,扩展类装载器,类路径类装载器,自定义类装载器.下面将围绕着这三s种类装载器做一个详细的描述:
1.启动类装载器:也叫引导类装装载器,它是JVM内部的类装载器,用C++编写,作用是装载Java核心类库中的类,比如说一个Java虚拟机目录为D:\JDK1.5,则启动类装载器负责装载D:\JDK1.5\jre\lib\rt.jar,rt.jar为Java核心类库,比如我们常用到的java.lang包中的System类,由于启动类装载器是供JVM使用的,Java程序并不能直接使用,下面举个例子来说明:
class Test {
public static void main(String[] args) {
System.out.println(System.class.getClassLoader());
}
}
上述程序打印出装载System类的类装载器,而打印出的却是null,说明了启动类装载器不能直接调用.
2.扩展类装载器:它负责装载Java的扩展类,比如说一个Java虚拟机目录为D:\JDK1.5,则它负责加载D:\JDK1.5\jre\lib\ext下的类库中的类,假如ext目录下有个jar包,包里有个Test类,包名为org.test,则可以用org.test.Test.class.getClassLoader()方法打印出该类加载器,可以看到结果为sun.misc.Launcher$ExtClassLoader,说明Test类由扩展类装载器装载,可以根据需要把自己写的类打成jar包放到ext目录下.
3.类路径类装载器:也可以叫系统类装载器,它负责装载当前classpath下的类,也就是当我们要运行一个Java程序时初始类的包所在的目录,比如在D:\test下有个Test.class,它是程序的初始类,当执行Java Test命令时D:\test就是classpath. 一般classpath下的类都是我们自定义的类,同样Test.getClassLoader()方法得到的结果是sun.misc.Launcher$AppClassLoader,说明Test类由类路径类装载器加载.
4.自定义类装载器:Java允许用户可以自定义类装载器来装载类,下面举个例子,使用URLClassLoader,从网络上装载一个类:
首先自己创建一个Java Web工程,Web根目录为WebRoot(WEB-INF的上一级目录),在WebRoot下创建一个包名为org.test.TestImpl的类,即TestImpl.class文件在WebRoot目录的org/test目录下,我们在定义一个接口,名为Test,接口有个test()方法,让Test实现它的test()方法,为了测试方便,把Test接口放在classes目录下:
Test类代码如下:
package org.test.TestImpl;
public class TestImpl implements Test{
public void add(){
System.out.println("TestImpl#add()");
}
}
在classes目录下写个测试类TestLoader :
public class TestLoader {
public static void main(String[] args) throws Exception{
URL url = new URL("http://localhost:8080/WebRoot/");
URLClassLoader loader = new URLClassLoader(new URL[]{url});
Class clazz = loader.loadClass("org.test.TestImpl");
Test t = (Test) clazz.newInstance();
t.add();
}
}
URLClassLoader从http://localhost:8080/WebRoot/下加载TestImpl,要执行TestLoader类,要把Web项目部署到Tomcat中,启动Tomcat后(上述的端口可以自己修改,tomcat的内容这里就不再敖述),执行TestLoader,可以看到程序打印出TestImpl#add(),说明加载成功. 我们可以在TestLoader的main方法中添加System.out.println(loader),结果答应出java.net.URLClassLoader,说明URLClassLoader为自定义类装载器.
二.由于除启动类装载器之外的所有类装载器都是由Java实现的,这些类加载器也是Java类,也需要用相应的类加载器来装载,那么各种类加载器分别是用什么加载器来加载呢?下面描述一下这个问题,请看如下代码,其中WebRequest是httpunit的包,我把它放到了ext目录下,用来测试扩展类装载器:
public class Test{
public static void main(String[] args) throws Exception{
ClassLoader loader0 = Thread.currentThread().getContextClassLoader();
URLClassLoader loader1 = new URLClassLoader(new URL[]{});
System.out.println(loader0);
System.out.println(loader0.getClass().getClassLoader());
System.out.println("======================");
System.out.println(loader1);
System.out.println(loader1.getClass().getClassLoader());
System.out.println("======================");
System.out.println(WebRequest.class.getClassLoader());
System.out.println(WebRequest.class.getClassLoader().getClass().getClassLoader());
}
}
上述代码中分别把装载类路径类装载器(系统类装载器)loader0,自定义类装载器loader1,扩展类装载器WebRequest.class.getClassLoader()这三个类装载器的类装载器打印出来,结果为:
sun.misc.Launcher$AppClassLoader@187c6c7
null
======================
java.net.URLClassLoader@7d772e
null
======================
sun.misc.Launcher$ExtClassLoader@10b62c9
null
从结果中看出,类路径类装载器,自定义类装载器,扩展类装载器都是由启动类装载器装载的.
三.一个类中如果引用了其他类,如果被引用的类不是Java核心类,不是Java扩展类,也不是从网络加载的类, 而在classpath下,则装载被引用类的类装载器是装载第一个的类装载器,如下代码说明这个问题:
public class Test{
public static void main(String[] args) throws Exception{
Test0 t0 = new Test0();
Test1 t1 = new Test1();
System.out.println(t0.getClass().getClassLoader());
System.out.println(t1.getClass().getClassLoader());
System.out.println(Thread.currentThread().getContextClassLoader());
}
}
Test0和Test1都在classpath下,最后得到当前线程的类装载器(装载Test的类装载器),打印结果为:
sun.misc.Launcher$AppClassLoader@187c6c7
sun.misc.Launcher$AppClassLoader@187c6c7
sun.misc.Launcher$AppClassLoader@187c6c7
从中看出得到的类装载器实例的hash码是相同的,说明都是用同一个类加载器加载的,也就是装载Test的类装载器.
四.除启动类装载器之外的任何类装载器有且仅有一个父加载器,扩展类装载器的父装载器为启动类装载器,类路径类装载器的父装载器为扩展类装载器,自定义类装载器的父装载器为类路径类装载器.如下代码说明:
public class Test{
public static void main(String[] args) throws Exception{
URLClassLoader loader0 = new URLClassLoader(new URL[]{});
System.out.println(loader0.getParent());
System.out.println(loader0.getParent().getParent());
System.out.println(loader0.getParent().getParent().getParent());
}
}
打印结果为:
sun.misc.Launcher$AppClassLoader@187c6c7
sun.misc.Launcher$ExtClassLoader@10b62c9
null
正好说明了上述的结论.
五.Java类装载器的委派机制:除启动类装载器外的任何类装载器都有一个默认的委派类装载器,比如:当一个自定义类装载器loader0试图去装载网络上的一个类时,它不会自己先去加载,而是先将加载任务委托给它的类路径类装载器,类路径类装载器又会将任务委托给扩展类装载器,扩展类装载器又会将任务委托给启动类装载器,由于在核心类库中找不到要加载的类,所以它告诉扩展类装载器没有加载到相应的类,这是扩展类装载器试图去加载,也没有找到,然后类路径类装载器在classpath也没找到.于是,loader0自己从网络上加载该类.从上面的描述中可以看出,启动类装载器优先去加载类.这样做的好处就是提高了安全性. 设想一下,如果有个恶意类,它的全限定名和Java核心类库中某个类相同,如java.lang.Integer,如果你的Java程序中使用了Integer这个类,如果没有这种委派机制的话,类路径类装载器会加载这个恶意类并执行,假如这个恶意Integer类中使用JNI来调用操作系统的API,那么后果可想而知. 不过通过委派机制的话,启动类装载器先去找java.lang.Integer,找到了之后,直接返回给类路径类装载器,这样类路径类装载器就没有机会去加载那个恶意的java.lang.Integer,这样就避免了一些安全问题.
上述写的可能不太直观,下面用代码说明:
假如我自己定义一个java.lang.Integer尝试去调用它:
package java.lang;
public class Integer {
public static void destroy(){
System.out.println("start destroy...");
}
}
测试类如下:
public class Test{
public static void main(String[] args) throws Exception{
Integer.destroy();
}
}
执行测试类并没有看到所期望的"start destroy...",而是一个异常:Exception in thread "main" java.lang.NoSuchMethodError: java.lang.Integer.destroy()V;这正好说明了启动类装载器已经装载了Integer并返回,程序中的Integer是Java核心类库中的类,当试图去调用destroy()方法时,由于Java核心类java.lang.Integer没有destroy()方法,所以报NoSuchMethodError.
先写到这里了...
一.Java中有四中类装载器:启动类装载器,扩展类装载器,类路径类装载器,自定义类装载器.下面将围绕着这三s种类装载器做一个详细的描述:
1.启动类装载器:也叫引导类装装载器,它是JVM内部的类装载器,用C++编写,作用是装载Java核心类库中的类,比如说一个Java虚拟机目录为D:\JDK1.5,则启动类装载器负责装载D:\JDK1.5\jre\lib\rt.jar,rt.jar为Java核心类库,比如我们常用到的java.lang包中的System类,由于启动类装载器是供JVM使用的,Java程序并不能直接使用,下面举个例子来说明:
class Test {
public static void main(String[] args) {
System.out.println(System.class.getClassLoader());
}
}
上述程序打印出装载System类的类装载器,而打印出的却是null,说明了启动类装载器不能直接调用.
2.扩展类装载器:它负责装载Java的扩展类,比如说一个Java虚拟机目录为D:\JDK1.5,则它负责加载D:\JDK1.5\jre\lib\ext下的类库中的类,假如ext目录下有个jar包,包里有个Test类,包名为org.test,则可以用org.test.Test.class.getClassLoader()方法打印出该类加载器,可以看到结果为sun.misc.Launcher$ExtClassLoader,说明Test类由扩展类装载器装载,可以根据需要把自己写的类打成jar包放到ext目录下.
3.类路径类装载器:也可以叫系统类装载器,它负责装载当前classpath下的类,也就是当我们要运行一个Java程序时初始类的包所在的目录,比如在D:\test下有个Test.class,它是程序的初始类,当执行Java Test命令时D:\test就是classpath. 一般classpath下的类都是我们自定义的类,同样Test.getClassLoader()方法得到的结果是sun.misc.Launcher$AppClassLoader,说明Test类由类路径类装载器加载.
4.自定义类装载器:Java允许用户可以自定义类装载器来装载类,下面举个例子,使用URLClassLoader,从网络上装载一个类:
首先自己创建一个Java Web工程,Web根目录为WebRoot(WEB-INF的上一级目录),在WebRoot下创建一个包名为org.test.TestImpl的类,即TestImpl.class文件在WebRoot目录的org/test目录下,我们在定义一个接口,名为Test,接口有个test()方法,让Test实现它的test()方法,为了测试方便,把Test接口放在classes目录下:
Test类代码如下:
package org.test.TestImpl;
public class TestImpl implements Test{
public void add(){
System.out.println("TestImpl#add()");
}
}
在classes目录下写个测试类TestLoader :
public class TestLoader {
public static void main(String[] args) throws Exception{
URL url = new URL("http://localhost:8080/WebRoot/");
URLClassLoader loader = new URLClassLoader(new URL[]{url});
Class clazz = loader.loadClass("org.test.TestImpl");
Test t = (Test) clazz.newInstance();
t.add();
}
}
URLClassLoader从http://localhost:8080/WebRoot/下加载TestImpl,要执行TestLoader类,要把Web项目部署到Tomcat中,启动Tomcat后(上述的端口可以自己修改,tomcat的内容这里就不再敖述),执行TestLoader,可以看到程序打印出TestImpl#add(),说明加载成功. 我们可以在TestLoader的main方法中添加System.out.println(loader),结果答应出java.net.URLClassLoader,说明URLClassLoader为自定义类装载器.
二.由于除启动类装载器之外的所有类装载器都是由Java实现的,这些类加载器也是Java类,也需要用相应的类加载器来装载,那么各种类加载器分别是用什么加载器来加载呢?下面描述一下这个问题,请看如下代码,其中WebRequest是httpunit的包,我把它放到了ext目录下,用来测试扩展类装载器:
public class Test{
public static void main(String[] args) throws Exception{
ClassLoader loader0 = Thread.currentThread().getContextClassLoader();
URLClassLoader loader1 = new URLClassLoader(new URL[]{});
System.out.println(loader0);
System.out.println(loader0.getClass().getClassLoader());
System.out.println("======================");
System.out.println(loader1);
System.out.println(loader1.getClass().getClassLoader());
System.out.println("======================");
System.out.println(WebRequest.class.getClassLoader());
System.out.println(WebRequest.class.getClassLoader().getClass().getClassLoader());
}
}
上述代码中分别把装载类路径类装载器(系统类装载器)loader0,自定义类装载器loader1,扩展类装载器WebRequest.class.getClassLoader()这三个类装载器的类装载器打印出来,结果为:
sun.misc.Launcher$AppClassLoader@187c6c7
null
======================
java.net.URLClassLoader@7d772e
null
======================
sun.misc.Launcher$ExtClassLoader@10b62c9
null
从结果中看出,类路径类装载器,自定义类装载器,扩展类装载器都是由启动类装载器装载的.
三.一个类中如果引用了其他类,如果被引用的类不是Java核心类,不是Java扩展类,也不是从网络加载的类, 而在classpath下,则装载被引用类的类装载器是装载第一个的类装载器,如下代码说明这个问题:
public class Test{
public static void main(String[] args) throws Exception{
Test0 t0 = new Test0();
Test1 t1 = new Test1();
System.out.println(t0.getClass().getClassLoader());
System.out.println(t1.getClass().getClassLoader());
System.out.println(Thread.currentThread().getContextClassLoader());
}
}
Test0和Test1都在classpath下,最后得到当前线程的类装载器(装载Test的类装载器),打印结果为:
sun.misc.Launcher$AppClassLoader@187c6c7
sun.misc.Launcher$AppClassLoader@187c6c7
sun.misc.Launcher$AppClassLoader@187c6c7
从中看出得到的类装载器实例的hash码是相同的,说明都是用同一个类加载器加载的,也就是装载Test的类装载器.
四.除启动类装载器之外的任何类装载器有且仅有一个父加载器,扩展类装载器的父装载器为启动类装载器,类路径类装载器的父装载器为扩展类装载器,自定义类装载器的父装载器为类路径类装载器.如下代码说明:
public class Test{
public static void main(String[] args) throws Exception{
URLClassLoader loader0 = new URLClassLoader(new URL[]{});
System.out.println(loader0.getParent());
System.out.println(loader0.getParent().getParent());
System.out.println(loader0.getParent().getParent().getParent());
}
}
打印结果为:
sun.misc.Launcher$AppClassLoader@187c6c7
sun.misc.Launcher$ExtClassLoader@10b62c9
null
正好说明了上述的结论.
五.Java类装载器的委派机制:除启动类装载器外的任何类装载器都有一个默认的委派类装载器,比如:当一个自定义类装载器loader0试图去装载网络上的一个类时,它不会自己先去加载,而是先将加载任务委托给它的类路径类装载器,类路径类装载器又会将任务委托给扩展类装载器,扩展类装载器又会将任务委托给启动类装载器,由于在核心类库中找不到要加载的类,所以它告诉扩展类装载器没有加载到相应的类,这是扩展类装载器试图去加载,也没有找到,然后类路径类装载器在classpath也没找到.于是,loader0自己从网络上加载该类.从上面的描述中可以看出,启动类装载器优先去加载类.这样做的好处就是提高了安全性. 设想一下,如果有个恶意类,它的全限定名和Java核心类库中某个类相同,如java.lang.Integer,如果你的Java程序中使用了Integer这个类,如果没有这种委派机制的话,类路径类装载器会加载这个恶意类并执行,假如这个恶意Integer类中使用JNI来调用操作系统的API,那么后果可想而知. 不过通过委派机制的话,启动类装载器先去找java.lang.Integer,找到了之后,直接返回给类路径类装载器,这样类路径类装载器就没有机会去加载那个恶意的java.lang.Integer,这样就避免了一些安全问题.
上述写的可能不太直观,下面用代码说明:
假如我自己定义一个java.lang.Integer尝试去调用它:
package java.lang;
public class Integer {
public static void destroy(){
System.out.println("start destroy...");
}
}
测试类如下:
public class Test{
public static void main(String[] args) throws Exception{
Integer.destroy();
}
}
执行测试类并没有看到所期望的"start destroy...",而是一个异常:Exception in thread "main" java.lang.NoSuchMethodError: java.lang.Integer.destroy()V;这正好说明了启动类装载器已经装载了Integer并返回,程序中的Integer是Java核心类库中的类,当试图去调用destroy()方法时,由于Java核心类java.lang.Integer没有destroy()方法,所以报NoSuchMethodError.
先写到这里了...
发表评论
-
关于无法delete文件
2011-09-26 17:12 1392File f=new File(targetDir+&qu ... -
正则表达式匹配替换网址
2011-08-30 13:10 3375有这么一个需求, 网页里所有的src="/web/ ... -
汉字编码
2011-07-29 11:08 908一、汉字编码的种类 汉字编码中现在主要用到的有三类, ... -
符编码笔记:ASCII,Unicode和UTF-8
2011-07-29 10:37 904阮一峰 日期: 2007年10月28日 1. ASCII码 ... -
Class.getResource
2011-07-21 13:19 989用JAVA获取文件,听似简单,但对于很多像我这样的新人来说, ... -
学习内部类的总结
2010-10-24 11:23 908public class TestInnerStatic ... -
Classloader加载类的问题探讨
2010-09-06 14:28 985首先在C:\Program Files\Java\jdk1.6 ... -
学习spring IOC AOP
2010-09-03 11:28 2412一.什么是控制反转模式? 不创建对象,但是描述创建它们的方 ... -
有符号类型转为无符号类型
2010-09-01 17:29 1402int toUnsigned(short s) { ... -
学习 utf-8总结
2010-08-15 16:45 819在ultraEditor中查看字符 “中国”的utf-8格式的 ... -
unicode
2010-08-15 16:02 1304Unicode是国际组织制定 ... -
关于集合的remove
2010-06-01 13:15 1084public class collectionsTest ... -
字符和整型的各种表示方法
2010-05-28 15:52 1385一个字符 的表示 '\u12ab'这种表示方法表示unico ... -
一个程序中String==String两种输入
2010-05-28 10:56 944今天帮别人写个程序 无意中发现的!! private v ... -
js中的if和Java中的if
2010-05-21 14:20 1541if(-323) { alert("if&q ... -
float和double精度
2010-05-19 10:55 2441float与double的范围和精度 1 范围 float和 ... -
子类重写父类的方法中有10种要求
2010-05-11 12:25 2280昨天发了一个帖子 parent.class ... -
the static method cannot hide instance method from object
2010-05-10 22:17 1250public class CloneInit implem ... -
proctected属性和方法的包依赖性
2010-05-08 20:34 1105这个说法是为理解自己造的 package testpro ... -
system.in.read
2010-04-28 10:34 1246关于字符的获得getBytes一直搞不明白!! 也就得过且过 ...
相关推荐
类装载器学习一、类加载器的基本概念 类装载器学习一、类加载器的基本概念 类装载器学习一、类加载器的基本概念
利用类装载器动态加载类并启动类,进行对文件的加密和解密
类装载器是Java 平台上最神秘,也是最 有趣的一个组,通过类装载器,除了可以实现程序的动态性之外,更能够做 到 无 懈 可 击 的 安 全 性
java jvm类装载器原理 介绍较为详细 大家可以参考
类装载器ClassLoader1
主要讲述Java的类装载器和命名空间,ClassLoader/parent delegation模型
深入JVM内核—原理、诊断与优化视频教程-6. 类装载器 深入JVM内核—原理、诊断与优化视频教程-6. 类装载器
Android基于类装载器插件架构的实现.pdf
java之jvm学习笔记五(实践写自己的类装载器)
[浅析J2EE应用服务器的JAVA类装载器]python回朔异常的模块.docx
类装载器
类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一。它使得 Java 类可以被动态加载到 Java 虚拟机中并执行。类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的。Java ...
java类装载介绍,介绍了java装载类的先后顺序
本书系《Java安全》第二版,内容涉及安全管理器、类装载器、存取控制器以及java.security包等。此外还讨论了消息摘要、证书和数字签名,并介绍了如何利用Java所提供的功能建立类签名,以及如何自行实现签名功能。...
Java虚拟机类装载111.docJava虚拟机类装载111.docJava虚拟机类装载111.doc
Java虚拟机包含一个类装载器,它可以从程序和 API中装载class文件。Java API中只有程序执行时需要的那些类才会被装载。字节码由执行引擎来执行。不同的Java虚拟机中,执行引擎可能实现得非常不同。在由软件实现的...
1:JVM内存模型:类加载机制【转载、验证、准备、解析、初始化】+类装载器【装载器分类、加载原则】+运行时数据区【方法区、堆、虚拟机栈、本地方法栈、程序计数器】。 2:垃圾回收:垃圾确定【引用计数法、可达性分析...
类的动态装载机制是JVM的一...本文介绍了JVM中类装载的原理、实现以及应用,尤其分析了ClassLoader的结构、用途以及如何利用自定义 的ClassLoader装载并执行Java类,希望能使读者对JVM中的类装载有一个比较深入的理解。
Java 类的动态装载机制是Java 虚拟机的一项核心技术,可以在运行时刻动态地加载或替换系统的 某些功能模块,而不影响系统...及应用,分析了类装载器的结构、用途,阐述了利用自定义类装载器装载并执行Java 类的过程。
在Java中,类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步,除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要工作如下: