做顿饭还要先买菜,用Java怎么能不懂类加载(一)
好戏开始:
逻辑思路(蛋生鸡)👇
1、类加载过程
多个java文件经过编译打包生成可运行jar包,最终由java命令运行某个主类的main函数启动程序,这里首先需要通过类加载器把主类加载到JVM。
主类在运行过程中如果使用到其它类,会逐步加载这些类。
注意,jar包里的类不是一次性全部加载的,是使用到时才加载。
类加载到使用整个过程有如下几步:
加载 >> 验证 >> 准备 >> 解析 >> 初始化 >> 使用 >> 卸载
加载:在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象等等
验证:校验字节码文件的正确性
准备:给类的静态变量分配内存,并赋予默认值
解析:将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用
初始化:对类的静态变量初始化为指定的值,执行静态代码块
实现落地(鸡生蛋)👇
2、类加载器和双亲委派机制
上面的类加载过程主要是通过类加载器来实现的,Java里有如下几种类加载器
启动类加载器: 负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsets.jar等
扩展类加载器: 负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包
应用程序类加载器:负责加载ClassPath路径下的类包,主要就是加载你自己写的那些类
自定义加载器: 负责加载用户自定义路径下的类包
看一个类加载器示例:
1 Public class ZhangjiangTestClassLoader{ 2 public static void main(String[] args){ 3 System.out.println(String.class.getClassLoader()); 4 System.out.println(com.sun.crypto.provideer.DESKeyFactory.class.getClassLoader().getClass().getClassName()); 5 System.out.println(ZhangjiangTestClassLoader.class.getClassLoader().getClass().getClassName()); 6 System.out.println(ClassLoader.getSystemClassLoader().getClass().getClassName()); 7 } 8 }
自定义一个类加载器示例:
自定义类加载器只需要继承 java.lang.ClassLoader 类,该类有两个核心方法,一个是loadClass(String, boolean),实现了双亲委派机制,大体逻辑
1. 首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。
2. 如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用parent.loadClass(name, false);).或者是调用bootstrap类加载器来加载。
3. 如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载。还有一个方法是findClass,默认实现是抛出异常,所以我们自定义类加载器主要是重写
findClass方法。
以下是自定义classLoader代码:
1 public class ZhangjiangClassLoaderTest { 2 static class ZhangjiangClassLoader extends ClassLoader { 3 private String classPath; 4 public ZhangjiangClassLoader(String classPath) { 5 this.classPath = classPath; 6 } 7 8 private byte[] loadByte(String name) throws Exception { 9 name = name.replaceAll("\\.", "/"); 10 FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class"; 11 int len = fis.available(); 12 byte[] data = new byte[len]; 13 fis.read(data); 14 fis.close(); 15 return data; 16 } 17 18 protected Class<?> findClass(String name) throws ClassNotFoundException{ 19 try { 20 byte[] data = loadByte(name); 21 //defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组 22 return defineClass(name, data, 0, data.length); 23 } catch (Exception e) { 24 e.printStackTrace(); 25 throw new ClassNotFoundException(); 26 } 27 } 28 } 29 30 public static void main(String args[]) throws Exception { 31 ZhangjiangClassLoader classLoader = new ZhangjiangClassLoader("D:/test"); 32 Class clazz = classLoader.loadClass("com.zhangjiang.jvm.User1"); 33 Object obj = clazz.newInstance(); 34 Method method= clazz.getDeclaredMethod("sout", null); 35 method.invoke(obj, null); 36 System.out.println(clazz.getClassLoader().getClass().getName()); 37 } 38 } 39 40 41 运行结果: 42 =======自己的加载器加载类调用方法======= 43 com.zhangjiang.jvm.ZhangjiangClassLoaderTest$ZhangjiangClassLoader
拓展
1.内网就安全吗?看这个
Java动态类加载,当FastJson遇上内网(博主建议看第3.4部分,第5部分为拓展)
转载链接:https://mp.weixin.qq.com/s/ou3L-IU1CNr9EGkpjH2u0w
2.好的知识是成体系的,脑图看这个
java 类的加载机制+思维导图+面试考题
转载链接:https://mp.weixin.qq.com/s/JabIenGFv0eIdgWbxp0Ieg
文中脑图:https://www.kdocs.cn/view/l/snSW5Qkk4?
3.功能时好时坏?代码错误是根源,排查你要借助类加载知识
从一次线上事故聊到类加载机制(博主建议,从王经理运维故障申报后的描述开始看,重点在loadClasss方法的源码展示部分到结束)
转载链接:https://mp.weixin.qq.com/s/ci3AWUXkB9UXaTjje-AtDQ
4.类加载子系统,用图看更明白
牛掰!20张图解密JVM类加载子系统,瞬间豁然开朗(博主建议:从3.3用户自定义加载器开始看)
转载链接:https://mp.weixin.qq.com/s/zJItCP0HI62Vmv1NXsPfew
5.依赖冲突怎么办?用类隔离啦
Java 类隔离加载的正确姿势(其实就是重写classLoader的findClass与loadClass方法)
转载链接:https://mp.weixin.qq.com/s/ZIKP48aU8_hnp8YbdA0b1g
6.碰到NoSuchMethodError怎么解决?看这个
用了这么久的 Java,你知道 NoSuchMethodError 是怎么产生的吗?(同时设计maven依赖仲裁机制)
转载链接:https://mp.weixin.qq.com/s/RTzuwrawXN_P4HOp-6-eKw


浙公网安备 33010602011771号