java虚拟机中,有一个类加载子系统,它包括了四种类加载器
1、根装载器(启动类装载器)2、扩展类装载器3、系统类装载器4、用户自定义类加载器
根加载器负责加载API里面的类,例如java.lang.object
扩展类装载器复制加载jre中ext包中的类,如C:\Program Files\Java\jdk1.6.0_21\jre\lib\ext
系统类装载器加载classpath中的类,记得我们配置JDK环境变量的时候如何配置classpath的吗?.;C:\Program Files\Java\jdk1.5.0_06\lib\tools.jar; C:\Program Files\Java\jdk1.5.0_06\lib\rt.jar,这些就是classpath里面的类,你程序中的类,也是由它加载的,因为你程序属于当前路径,classpath中有个“.”
补充个知识点(摘至百度):
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------
rt.jar是JAVA基础类库,dt.jar是关于运行环境的类库,tools.jar是工具类库
设置在classpath里是为了让你 import *
---------------------------------------------------------------
web系统都用到tool.jar
你用winrar看看里面是什么内容啦
---------------------------------------------------------------
1.
rt.jar 默认就在 根classloader的加载路径里面 放在claspath是多此一举
不信你可以去掉classpath里面的rt.jar
然后用 java -verbose XXXX 的方式运行一个简单的类 就知道 JVM的系统根Loader的路径里面
不光rt.jar jre\lib下面的大部分jar 都在这个路径里
2.
tools.jar 是系统用来编译一个类的时候用到的 也就是javac的时候用到
javac XXX.java
实际上就是运行
java -Calsspath=%JAVA_HOME%\lib\tools.jar xx.xxx.Main XXX.java
javac就是对上面命令的封装 所以tools.jar 也不用加到classpath里面
3.
dt.jar是关于运行环境的类库,主要是swing的包 你要用到swing时最好加上
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
类装载器是如何装载类的呢?
类装载器采用了父亲委派模式来进行加载,父亲委派模式是这样的,要加载一个类的时候,首先叫负责加载该类的类装载器的父装载器去尝试加载,如果加载不了,再往上抛,一直抛到根加载器,如果根加载器还加载不了,那么就让负责加载该类的类装载器来加载。
根装载器没有父装载器,它是C实现的,所以在程序中获取不到它,扩展类装载器的父装载器是根装载器,系统类装载器的父装载器是扩展类装载器,而用户自定义的类装载器,默认情况下是系统类装载器,当然,在自定义类装载器的时候,是可以指定父装载器的。
讲完了加载的顺序,那么我们就来尝试着写一个类装载器,让它装载固定某个地方的类
首先写一个自己的类加载器,MyClassLoader.java,继承ClassLoader类,然后重写findClass方法:
package com.wyp12.myClassLoader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 自己写一个类加载器 ,去加载"d:\myclass\com\wyp12\*.class"
*
* @author prince
*
*/
public class MyClassLoader extends ClassLoader {
private String name;
public MyClassLoader(String name) {
super(); // 通过这个构造方法生成的类加载器,它的父加载器是系统类加载器
this.name = name;
}
public MyClassLoader(String name, ClassLoader loader) {
super(loader); // 通过这个这个构造方法生成的类加载器,该加载器的父加载器是loader,如果为空,则父加载器为根加载器
// 子类继承父类,如果不显式写出调用父类的哪个构造方法,那么就默认调用父类的无参构造函数
this.name = name;
}
public String toString()
{
return this.name;
}
// 要重写findclass这个方法,loadclass会调用它
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
byte[] data = null;
FileInputStream fis = null;
try {
fis = new FileInputStream("d:\\myclass\\com\\wyp12\\myClassLoader\\"+name+".class");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
ByteArrayOutputStream abos = new ByteArrayOutputStream();
int ch = 0;
try {
while (-1!=(ch=fis.read()))
{
abos.write(ch); //把字节一个一个写到输出流中
}
} catch (IOException e) {
e.printStackTrace();
}
data = abos.toByteArray(); //把输出流中的字节弄成一个字节数组
return this.defineClass("com.wyp12.myClassLoader."+name,data, 0, data.length,null);
}
}这个类加载器我让它默认加载d:\myclass\com\wyp12\myClassLoader路径下的class文件,具体文件名到时候我们构造实例的时候再给定,下面是测试类:import com.wyp12.myClassLoader.MyClassLoader;
public class TestLoader {
public static void main(String[] args) throws Exception {
MyClassLoader l1 = new MyClassLoader("loader1");
Class dogC = l1.loadClass("Dog");
dogC.newInstance();
/*MyClassLoader l2 = new MyClassLoader("loader2",l1); //把L1作为它的父加载器
Class doccc = l2.loadClass("Dog");
doccc.newInstance(); */
}
}下面是要加载的类的源代码,很简答,就是让它打印出是谁加载了他
package com.wyp12.myClassLoader;
public class Dog {
public Dog()
{
System.out.println(this.getClass().getClassLoader());
}
}
运行结果:
loader1
下面贴上ClassLoader的主要源代码:
它的几个构造函数
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}通过几个函数,把字节码数组转换成一个CLASS实例
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
return defineClassCond(name, b, off, len, protectionDomain, true);
}
// Private method w/ an extra argument for skipping class verification
private final Class<?> defineClassCond(String name,
byte[] b, int off, int len,
ProtectionDomain protectionDomain,
boolean verify)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain);
Class c = null;
String source = defineClassSourceLocation(protectionDomain);
try {
c = defineClass1(name, b, off, len, protectionDomain, source,
verify);
} catch (ClassFormatError cfe) {
c = defineTransformedClass(name, b, off, len, protectionDomain, cfe,
source, verify);
}
postDefineClass(c, protectionDomain);
return c;
}下面这个是loadClass()
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}下面是我们要重写的方法:
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}基本就这样了,其实还很多细节,由于自己的文笔也不是很好,就不写出来了,只是做个mark,以后便于复习
本文详细介绍了Java虚拟机中的类加载子系统,包括根装载器、扩展类装载器、系统类装载器及其工作原理。并通过自定义类加载器的实现,展示了如何在特定路径下加载类文件。同时,解释了类装载器的委派模式,并提供了关键源代码分析。
2019





