首先说我们写的java代码(xxx.java文件)如何被jvm使用?
通过javac 编译为可被虚拟机识别的机器码(二进制),然后通过类加载器将二进制文件放入内存,这个过程称为内的加载过程,而加载类(xxx.java文件)的类被称作类加载器,实际也是有java代码写的一个类(除了引导类加载器,该加载器有C++编写)
1、类加载器结构图如下(该图来自于网络 https://blog.csdn.net/p10010/article/details/50448491 )
可以看到,自定义类加载器在最下面,表示当一个类需要使用用户自定义类加载器时,先把这个类交给其父类即应用程序类加载器,当 应用程序类加载器 拿到这个类后不管三七二十一,先交给自己的"父类"即扩展类加载器,扩展类加载器一样,先交给启动类加载器,当然启动类加载器是最顶级的加载器了,自然需要看看自己能不能加载这个类,如果能就自己加载并把加载后的结果(java.lang,Calss对象)返回,否则给自己的子类让其加载,以此类推,这就是双亲委托机制
其好处是安全,各个类加载的主要加载对象如下:
启动类加载器只加载特定位置的类(%JAVA_HOME%\jre\lib\rt.jar)里面的类(这也是java的核心类库)
扩展类加载器只加载 %JAVA_HOME%\jre\lib\ext\下的类,ext=>extend
应用类加载器在一般情况下回加载其他所有类路径下的类
用户自定义类加载器用于特定功能的类加载(比如要加密某个类)
2、类加载的流程如下(图片来自网络https://www.cnblogs.com/wxd0108/p/6681618.html)
3、具体代码如下
package com.lyzx.jvm;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class MyClassLoad extends ClassLoader{
private final String rootDir;
public MyClassLoad(String rootDir){
this.rootDir = rootDir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("调用findClass("+name+")");
Class<?> c = findLoadedClass(name);
//应该要先查询有没有加载过这个类。如果已经加载,则直接返回加载好的类。如果没有,则加载新的类。
if(c != null){
System.out.println("类"+name+"已经被加载,不需要重复加载!");
return c;
}else{
ClassLoader parent = this.getParent();
try {
c = parent.loadClass(name); //委派给父类加载
} catch (Exception e){
System.out.println("父类没有加载到["+name+"]这个类");
e.printStackTrace();
}
if(c != null){
System.out.println("父类加载器加载到["+name+"]这个类");
return c;
}else{
byte[] classData = getClassData(name);
if(classData == null){
throw new ClassNotFoundException();
}else{
c = defineClass(name, classData, 0,classData.length);
System.out.println("自定义类加载器加载类["+name+"]");
}
}
}
return c;
}
private byte[] getClassData(String classname){ //com.bjsxt.test.User d:/myjava/ com/bjsxt/test/User.class
String path = rootDir +"/"+ classname.replace('.', '/')+".class";
// IOUtils,可以使用它将流中的数据转成字节数组
InputStream is = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try{
is = new FileInputStream(path);
byte[] buffer = new byte[1024];
int temp=0;
while((temp=is.read(buffer))!=-1){
baos.write(buffer, 0, temp);
}
return baos.toByteArray();
}catch(Exception e){
e.printStackTrace();
return null;
}finally{
try {
if(is!=null){
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(baos!=null){
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
测试代码如下
package com.lyzx.jvm;
public class JvmTest {
public static void main(String[] args) throws ClassNotFoundException {
MyClassLoad loader = new MyClassLoad("D:\\testJvm");
Class<?> c = loader.loadClass("com.lyzx.jvm.JvmLearn");
System.out.println(">>>"+c.getClassLoader());
}
}