Java学习之 ClassLoader类加载器
0x00 前言
前面这里抛出一个问题,Java到底是什么类型的编程语言?是编译型?还是解释型?在这个问题是其实一直都都有疑惑,如果说是解释型语言的话,那么为什么需要编译呢?如果说是编译型语言的话,那么在编译完成后,需要JVM去解析才能运行呢?其实两种说法都对,也不对。下面来看看java的一个执行流程就知道是怎么回事了。
0x01 类加载机制
Java中的源码.java 后缀文件会在运行前被编译成.class 后缀文件,Java类初始化的时候会调用java.lang.ClassLoader 加载字节码,.class 文件中保存着Java代码经转换后的虚拟机指令,当需要使用某个类时,虚拟机将会加载它的.class 文件,并创建对应的class对象,将class文件加载到虚拟机的内存。
在其中其实包含了比较多的内容,下面来看看他的详细执行流程。
具体的实现分为三大步骤:
第一步 加载:
类加载指的是将class文件读入内存,并为之创建一个java.lang.Class对象,即程序中使用任何类时,系统都会为之建立一个java.lang.Class对象,系统中所有的类都是java.lang.Class的实例。
类的加载由类加载器完成,JVM提供的类加载器叫做系统类加载器,此外还可以通过继承ClassLoader基类来自定义类加载器。
第二步 连接:
连接阶段负责把类的二进制数据合并到JRE中
其又可分为如下三个阶段:
验证:确保加载的类信息符合JVM规范,无安全方面的问题。
准备:为类的静态Field分配内存,并设置初始值。
解析:将类的二进制数据中的符号引用替换成直接引用。
第三步 初始化:
类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量(如前面只初始化了默认值的static变量将会在这个阶段赋值,成员变量也将被初始化
双亲委托机制
当一个类加载器查找class和resource时,是通过“委托模式”进行的,它首先会判断这个class是不是已经加载成功,如果没有加载的话它并不是自己进行查找,而是先通过父加载器,然后递归下去,直到Bootstrap ClassLoader,如果Bootstrap classloader找到了,直接返回,如果没有找到,则一级一级返回,最后是由自身去查找这些对象;这种机制就叫做双亲委托。
其他的方式也可以去加载类的二进制数据:
1.从本地文件系统加载class文件。
2.从JAR包中加载class文件,如JAR包的数据库启驱动类。
3.通过网络加载class文件。
4.把一个Java源文件动态编译并执行加载。
0x02 ClassLoader类加载器
前面提到过编译成class字节码后的文件,会使用类加载器加载字节码。也就是说在java中所有的类都会通过加载器进行加载才能运行。在JVM类加载器中最顶层的是Bootstrap ClassLoader(引导类加载器) 、Extension ClassLoader(扩展类加载器) 、App ClassLoader(系统类加载器) ,AppClassLoader 是默认的类加载器,如果类加载时我们不指定类加载器的情况下,默认会使用AppClassLoader 加载类。

ClassLoader类 核心方法:
1.loadClass(String className),根据名字加载一个类。
2.defineClass(String name,byte[] b,int off,int len),将一个字节流定义为一个类。
3.findClass(String name),查找一个类。
4.findLoadedClass(String name),在已加载的类中,查找一个类。
5.resolveClass(链接指定的Java类)
0x03 自定义Classloader加载class文件
在ClassLoader中有四个很重要实用的方法loadClass()、findLoadedClass()、findClass()、defineClass(),可以用来创建属于自己的类的加载方式;比如我们需要动态加载一些东西,或者从C盘某个特定的文件夹加载一个class文件,又或者从网络上下载class主内容然后再进行加载等。分三步搞定:
1、编写一个类继承ClassLoader抽象类;
2、重写findClass()方法;
3、在findClass()方法中调用defineClass()方法即可实现自定义ClassLoader;
下面来编写一个test类
package com.test;
public class test {
public String method(){
return "hello,world";
}
}
编写完成后使用javac进行编译为class字节码文件
javac .test.java

会发现多出一个class字节码文件,那么我们需要再对其转换为byte类型,方便后面使用类加载器进行加载执行。
import sun.misc.IoUtils;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
public class ulits {
public static void main(String[] args) throws IOException {
InputStream fis = new FileInputStream("test.class");
byte[] bytes = IoUtils.readFully(fis,-1,false);
System.out.println(Arrays.toString(bytes));
}
}

自定义加载器类:
package com.test;
import java.lang.reflect.Method;
public class classloadertest extends ClassLoader{
private static String testclassname= "com.test.test";
//转换byte后的字节码
private static byte[] classbytes= new byte[]{-54,-2,-70,-66,52,29,10,6,15,9,16,17,8,18,19,20,7,21,22,1,60,105,110,116,62,3,40,41,86,4,67,111,100,101,76,78,117,109,98,114,84,97,108,91,106,118,47,103,83,59,99,70,115,46,12,23,24,25,-23,-114,-75,-47,-122,-18,-108,-111,-76,-26,-124,-84,-27,-89,-101,26,27,28,13,79,121,80,112,33,5,2,42,-73,-79,11,37,-78,-74,14};
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
//只处理com.test.test类
if (name.equals(testclassname)) {
//将一个字节流定义为一个类。
return defineClass(testclassname,classbytes,classbytes.length);
}
return super.findClass(name);
}
public static void main(String[] args) throws Exception {
//创建加载器
classloadertest classloadertest = new classloadertest();
//使用我们自定义的类去加载testclassname
Class aClass = classloadertest.loadClass(testclassname);
//反射创建test类对象
Object o = aClass.newInstance();
//反射获取method方法
Method method = o.getClass().getMethod("method");
//反射去调用执行method方法
String str = (String) method.invoke(o);
System.out.println(str);
}
}

可以看到我们class中的method方法执行了。
总结
1、java文件编译成class字节码文件
2、转换字节码文件为byte类型,目的方便存储,加载执行
3、自定义一个类加载器类,自定义处理流程(findClass),实现对象的调用。过程为创建对象,加载class,反射创建自定义class的对象,反射获取需要执行的方法,执行方法。
参考文章
https://javasec.org/javase/ClassLoader/
https://blog.csdn.net/javazejian/article/details/73413292
https://blog.csdn.net/CNAHYZ/article/details/82219210
https://blog.csdn.net/briblue/article/details/54973413
https://blog.csdn.net/xyang81/article/details/7292380
0x04 结尾
对于这篇文章,对于没了解过类加载器的来说还是比较吃力的,比如我。在文中有些地方写的也比较模糊,所以贴了几个不错的文章在上面,作为参考。 (编辑:北几岛)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|