Java 类加载机制,双亲委派模型和实现自己的类加载器
类加载可以看这个博客http://www.importnew.com/25295.html双亲委派模型启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。扩展类加载器(Extension ClassLoader):...
类加载可以看这个博客
http://www.importnew.com/25295.html
双亲委派模型
启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。
扩展类加载器(Extension ClassLoader):负责加载 JAVA_HOME\lib\ext 目录中的,或通过java.ext.dirs系统变量指定路径中的类库。
应用程序类加载器(Application ClassLoader):负责加载用户路径(classpath)上的类库。
抄的上面那个博客。。
当一个类加载器收到类加载任务,会先交给其父类加载器去完成,因此最终加载任务都会传递到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务。
来看类加载的代码
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
//查看类是否已经被加载
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
//父加载器不为空,调用父
c = parent.loadClass(name, false);
} else {
//为空,调用BootStrap加载器
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.
//父类没有加载成功,自己本身进行加载
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
//此处为类加载的解析阶段
resolveClass(c);
}
return c;
}
}
大致过程
1.查看这个类是否已经被加载
2.若未加载,则查看父类加载器是否为空,为空就直接调用启动类加载器,不为空则调用父类加载器
3.若第二步没有成功,则自己进行加载, 也就是调用自己的findclass方法
实现自己的类加载器
1.首先写一个类
package cn.dengbin97.myclass;
public class Solution {
public static void main(String[] args) {
}
}
2.对类进行编译,放到一个目录
//此处由于指定了包,所以不能直接在当前目录编译
//package cn.dengbin97.myclass;
//像上面这样的就要返回到cn目录进行编译
C:\temp\cn>javac C:\temp\cn\dengbin97\myclass\Solution.java
3.定义自己的类加载器,重写findclass方法
若要去破坏双亲委派原则,可以重写loadclass方法,最好还是不要
package main;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class MyClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
//获取文件字节流
byte[] bytes = getClassBytes(new File("C:\\temp\\cn\\dengbin97\\myclass\\Solution.class"));
//把字节流转化为Class对象
Class<?> c = this.defineClass(name, bytes, 0, bytes.length);
return c;
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
/**
* 获取文件的字节流
* */
private byte[] getClassBytes(File file) {
byte[] tempbytes = new byte[102400];
byte[] res = null;
InputStream in = null;
try {
in = new FileInputStream(file);
//此处为了方便,直接一次读取了,可以进行改进
int read = in.read(tempbytes);
res = new byte[read];
//把读取结果拷贝的新的数组,保证新数组不留空,因为后面要用到字节流的大小,可以从数组获取
System.arraycopy(tempbytes, 0, res, 0, read);
} catch (Exception e1) {
e1.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e1) {
}
}
}
return res;
}
}
4.进行测试
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
MyClassLoader loader = new MyClassLoader();
//使用Class.forName进行类加载
//指定类和使用的加载器
Class<?> clazz = Class.forName("cn.dengbin97.myclass.Solution", true, loader);
Object newInstance = clazz.newInstance();
//获取类实例
System.out.println(newInstance);
//获取类
System.out.println(newInstance.getClass());
//获取使用的加载器
System.out.println(newInstance.getClass().getClassLoader());
}
//运行结果
cn.dengbin97.myclass.Solution@70dea4e
class cn.dengbin97.myclass.Solution
//此处可以看到,类加载器使用的是我们自己的
main.MyClassLoader@7852e922
loadClass:检查类是否已经加载,调用父类或自己本身进行类的加载
findClass:真正实现类的加载,获取字节码文件的字节流,调用defineClass得到class对象
defineClass:将读取的二进制字节码文件转化为Class对象
更多推荐
所有评论(0)