本文主要说一下一个类从编译到执行的过程,现在有一个普通的Person类。
从编译到执行这段时间内,Person执行了三个阶段:
- 编译器将Person.java源文件编译为Person.class字节码文件;
- ClassLoader将字节码转换为JVM中的Class< Person >对象;
- JVM利用Class< Person >对象实例化为Person对象。
已经了解了一个类要经过以上三个步骤去运行,现在深入来了解一下ClassLoader是怎么将字节码转换成JVM能够识别的格式。
ClassLoader
ClassLoader在Java中有着非常重要的作用,主要工作在Class装载的加载阶段,其主要作用是从系统外部获得Class二进制数据流。它是Java核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过将Class文件里的二进制数据流装载进系统,然后交给Java虚拟机进行连接、初始化等操作。
ClassLoader内部定义了关于类加载流程和类加载方式的方法,其中最重要的方法就是loadClass通过这个方法才能加载到类:
上图是loadClass源码,看到里面有一个parent,这个parent类型也是一个ClassLoader,ClassLoader类加载器并不是只有一种,下面来介绍几种常见的类加载器:
- BootStrapClassLoader:C++编写,JVM内核实现,加载Java最核心的库java.*
- *ExtClassLoader:Java编写,用来记载扩展库javax.和自己编写的jar包
源码中有个getExtDirs是来获取class文件的:
System.out.println(System.getProperty("java.ext.dirs"));
///Users/xxx/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
此路径的意义就是使ExtClassLoader根据这个路径找到对应的class文件去进行加载。
- AppClassLoader:Java编写,加载程序所在目录
这个累加载器主要是用来加载类路径就是ClassPath
可以通过这个路径去找到daily,通过daily路径找到编写的class路径去进行加载。
- 自定义ClassLoader:Java编写,定制化进行加载
自定义类加载器就是不光加载Java文件或者class文件,自定义的类加载器有很多用处通过覆盖两个关键的方法可以实现自定义的ClassLoader。
findClass:
defineClass:
在自定义类加载器的时候可以通过写入一个路径去进行加载,找不到就抛出异常,找到就进行defineClass,通过传入的class的字节流返回class对象。
下面是一个实例演示:
先编写自定义的ClassLoader:
public class MyClassLoader extends ClassLoader{
private String path;
private String classLoaderName;
public MyClassLoader(String path, String classLoaderName) {
this.path = path;
this.classLoaderName = classLoaderName;
}
//找寻类文件
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
//加载类文件
private byte[] loadClassData(String name) {
name = path + name + ".class";
InputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(new File(name));
out = new ByteArrayOutputStream();
int index = 0;
while ((index = in.read()) != -1){
out.write(index);
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
in.close();
out.close();
}catch (Exception e){
e.printStackTrace();
}
}
return out.toByteArray();
}
}
编写一个测试类查看是否能读到class文件:
public class ClassLoaderCheck {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
MyClassLoader myClassLoader = new MyClassLoader("/Users/xiaotongxin/Desktop/", "myClassLoader");
Class<?> loaderClass = myClassLoader.findClass("Ljy");
System.out.println(loaderClass.getClassLoader());
loaderClass.newInstance();
}
}
运行结果如下: