Java字节码简单说,Java字节码就是.class后缀的文件,里面存放Java虚拟机执行的指令 。由于Java是一门跨平台的编译型语言 , 所以可以适用于不同平台 , 不同CPU的计算机 , 开发者只需要将自己的代码编译一次,就可以运行在不同平台的JVM中 。甚至,开发者可以用类似Scala、Kotlin这样的语言编写代码,只要你的编译器能够将代码编译成.class文件,都可以在JVM虚拟机中运行:uploading-image-878441.png
URLClassLoader加载远程class文件
ClassLoader是一个加载器,就是用来告诉JVM虚拟机如何去加载这个类,默认的就是根据类名来加载类,这个类名需要是完整路径,比如说java.lang.RuntimeURLClassLoader 实际上是我们平时默认使用的 AppClassLoader 的父类,所以,我们解释URLClassLoader 的工作过程实际上就是在解释默认的Java类加载器的工作流程
正常情况下,Java会根据配置项 sun.boot.class.path 和 java.class.path 中列举到的基础路径(这些路径是经过处理后的 java.net.URL 类)来寻找.class文件来加载,而这个基础路径有分为三种情况:
- URL未以斜杠 / 结尾 , 则认为是一个JAR文件,使用 JarLoader 来寻找类,即为在Jar包中寻找.class文件
- URL以斜杠 / 结尾,且协议名是 file,则使用 FileLoader 来寻找类,即为在本地文件系统中寻找.class文件
- URL以斜杠 / 结尾,且协议名不是 file,则使用最基础的 Loader 来寻找类
public class Hello { public Evil() throws Exception { }}利用ClassLoader#defineClass直接加载字节码其实,不管是加载远程class文件 , 还是本地的class或jar文件 , Java都经历的是下面这三个方法调用
ClassLoader#loadClass ---> ClassLoader#findClass ---> ClassLoader#defineClass
- loadClass 的作用是从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机制) , 在前面没有找到的情况下,执行 findClass
- findClass 的作用是根据基础URL指定的方式来加载类的字节码,就像上面说到的 , 可能会在本地文件系统、jar包或远程http服务器上读取字节码,然后交给 defineClass
- defineClass 的作用是处理前面传入的字节码,将其处理成真正的Java类
native方法称为本地方法 。在java源程序中以关键字“native”声明,不提供函数体 。其实现使用C/C++语言在另外的文件中编写,编写的规则遵循Java本地接口的规范(简称JNI) 。简而言就是Java中声明的可调用的使用C/C++实现的方法 。例子:
package org.gk0d;import java.lang.reflect.Method;import java.util.Base64;public class HelloDefineClass { public static void main(String[] args) throws Exception { Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); defineClass.setAccessible(true); byte[] code = Base64.getDecoder().decode("yv66vgAAADQAGwoABgANCQAOAA8IABAKABEAEgcAEwcAFAEA"+ "Bjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApTb3VyY2VGaWxlAQAKSGVs"+ "bG8uamF2YQwABwAIBwAVDAAWABcBAAtIZWxsbyBXb3JsZAcAGAwAGQAaAQAFSGVsbG8BABBqYXZh"+ "L2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3Ry"+ "ZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5n"+ "OylWACEABQAGAAAAAAABAAEABwAIAAEACQAAAC0AAgABAAAADSq3AAGyAAISA7YABLEAAAABAAoA"+ "AAAOAAMAAAACAAQABAAMAAUAAQALAAAAAgAM"); Class hello = (Class)defineClass.invoke(ClassLoader.getSystemClassLoader(), "Hello", code,0, code.length); hello.newInstance(); }}//ClassLoader.getSystemClassLoader()返回系统的类加载器对象
推荐阅读
- JavaScript常用工具函数
- uniapp之uni-starter小程序多端研发框架搭建与项目实践
- MySQL数据库的性能分析 ---图书《软件性能测试分析与调优实践之路》-手稿节选
- Java安全之CC6
- 如何通过Java导出带格式的 Excel 数据到 Word 表格
- 支持JDK19虚拟线程的web框架之四:看源码,了解quarkus如何支持虚拟线程
- vulnhub靶场之DRIPPING BLUES: 1
- 明日之后联盟狙击步枪怎么获取
- 硬核剖析Java锁底层AQS源码,深入理解底层架构设计
- 定位java程序中占用cpu最高的线程堆栈信息