字节码编程ASM之两数之和

写在前面

源码 。
看下如何使用ASM来写如下的类:

package com.dahuyou.demo.asm;

public class AsmSumOfTwo {
    public AsmSumOfTwo() {
    }

    public static void main(String[] var0) {
        int var1 = (new AsmSumOfTwo()).sum(1, 2);
        System.out.println(var1);
    }

    public int sum(int var1, int var2) {
        return var1 + var2;
    }
}

1:编码

源码:

package com.dahuyou.asm.helloworld;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;

public class SumOfTwo extends ClassLoader {

    public static void main(String[] args) throws Exception {
        // 生成二进制字节码
        byte[] bytes = generate();
        // 输出字节码
        outputClazz(bytes);
        // 加载AsmHelloWorld
        Class<?> clazz = new SumOfTwo().defineClass("com.dahuyou.demo.asm.AsmSumOfTwo", bytes, 0, bytes.length);
        // 反射获取 main 方法
        Method main = clazz.getMethod("main", String[].class);
        // 调用 main 方法
        main.invoke(null, new Object[]{new String[]{}});
    }

    private static byte[] generate() {

        ClassWriter classWriter = new ClassWriter(0);

        {
            MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
            mv.visitCode();
            // aload指令,加载本地变量表0位置对象(a)
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            // invokespecial指令,调用方法<init>
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
            // return指令,返回void
            mv.visitInsn(Opcodes.RETURN);
            // 操作数栈,局部变量表大小
            mv.visitMaxs(1, 1);
            mv.visitEnd();
        }

        // 定义对象头;版本号、修饰符、全类名、签名、父类、实现的接口
        classWriter.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC, "com/dahuyou/demo/asm/AsmSumOfTwo", null, "java/lang/Object", null);
        // 添加方法;修饰符、方法名、描述符、签名、异常
        MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
        // int var1 = (new AsmSumOfTwo()).sum(1, 2);中的 new AsmSumOfTwo(),即创建实例对象,并压入操作数栈栈顶
        methodVisitor.visitTypeInsn(Opcodes.NEW, "com/dahuyou/demo/asm/AsmSumOfTwo");
        // 获取操作数栈栈顶元素,并复制,重新压回,此时操作数栈栈顶有连续的两个new AsmSumOfTwo()引用
        methodVisitor.visitInsn(Opcodes.DUP);
        // 栈顶元素出栈,并调用初始化方法<init>,即默认的空构造函数
        methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, "com/dahuyou/demo/asm/AsmSumOfTwo", "<init>", "()V");
        // iconst 加载整数常量 1,2,并压到栈顶,此时,操作数栈元素为2,1,AsmSumOfTwo实例引用
        methodVisitor.visitInsn(Opcodes.ICONST_1);
        methodVisitor.visitInsn(Opcodes.ICONST_2);

        // int var1 = (new AsmSumOfTwo()).sum(1, 2);中的.sum(1, 2) (II)I入参两个int,返回int,函数的结果存储栈顶
        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "com/dahuyou/demo/asm/AsmSumOfTwo", "sum", "(II)I");
        // 将栈顶元素存储到本地变量表 slot 1位置,即赋值给var1(实际程序运行过程中jvm是不关心变量名称的,因为完全不影响运行结果)
        methodVisitor.visitVarInsn(Opcodes.ISTORE, 1);
        // 获取静态属性System.out
        methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        // 将本地变量表中slot 1位置的int变量推动到操作数栈栈顶(因为要做sout)
        methodVisitor.visitVarInsn(Opcodes.ILOAD, 1);
        // 获取栈顶元素,并作sout,这样int var1 = (new AsmSumOfTwo()).sum(1, 2);的var1就完成打印了
        // (I)V参数int,返回void
        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V");
        // 返回
        methodVisitor.visitInsn(Opcodes.RETURN);

        // 设置操作数栈的深度和局部变量的大小
        methodVisitor.visitMaxs(3, 2);
        // 方法结束
        methodVisitor.visitEnd();

        /**
         * 以下代码定义如下的函数:
         * public int sum(int i, int m) {
         *     return i + m;
         * }
         */
        MethodVisitor methodVisitor_sum = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "sum", "(II)I", null, null);
        // 加载本地变量表slot 1,2位置int变量到操作数栈,并调用add指令执行加法
        methodVisitor_sum.visitVarInsn(Opcodes.ILOAD, 1);
        methodVisitor_sum.visitVarInsn(Opcodes.ILOAD, 2);
        methodVisitor_sum.visitInsn(Opcodes.IADD);
        // 返回
        methodVisitor_sum.visitInsn(Opcodes.IRETURN);
        // 设置操作数栈的深度和局部变量的大小
        methodVisitor_sum.visitMaxs(2, 3);
        methodVisitor_sum.visitEnd();

        // 类完成
        classWriter.visitEnd();
        // 生成字节数组
        return classWriter.toByteArray();
    }

    private static void outputClazz(byte[] bytes) {
        // 输出类字节码
        FileOutputStream out = null;
        try {
            out = new FileOutputStream("AsmSumOfTwo.class");
            System.out.println("ASM类输出路径:" + (new File("")).getAbsolutePath());
            out.write(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != out) try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

写了非常详细的注释,看下吧,还有哪里不懂的可以留言告知。然后运行下:
在这里插入图片描述
反编译看对应的Java代码:
在这里插入图片描述

写在后面

一个多么简单的加法函数,通过底层字节码方式来编写还是挺麻烦的,只能说底层还是很复杂的,但是想要进阶,不了解底层又是不行的。所以,在这winter already coming的行业环境下,加油吧!!!

参考文章列表

JVM 虚拟机字节码指令表 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/751921.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++精解【8】

文章目录 运算,- 加减法* / 乘除法逐元 乘法逐元 除法逐元综合运算矩阵乘法与加减法 转置、共轭、伴随矩阵点乘法,叉积 运算 ,- 加减法 逐元加减法 #include <iostream> #include "e:/eigen/Eigen/Dense" using namespace std;int main() {Eigen::Matrix2d …

【源码】Spring Data JPA原理解析之Auditing执行原理

Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 5、Spring Data JPA自定…

vue + Lodop 制作可视化设计页面 实现打印设计功能(二)

历史&#xff1a; vue2 Lodop 制作可视化设计页面 实现打印设计功能&#xff08;一&#xff09; 前言&#xff1a; 之前本来打算用直接拿之前做的vue2版本改改就发的&#xff0c;但考虑到现在主流都是vue3了&#xff0c;所以从这篇文章开始使用vue3来写&#xff0c;以及最后…

Spring相关面试题(三)

29 如何在所有的BeanDefinition注册完成后&#xff0c;进行扩展 Bean工厂的后置处理器&#xff0c;在所有的Bean注册完成后&#xff0c;就被执行。 public class A implements BeanFactoryPostProcessor {private String name "a class";private B b; ​public St…

项目分层--简单图书管理系统

分层情况 实体类Book代码 //实体类 public class Book {private int id;private String name;private int bsum;public Book() {}public Book(int id, String name, int bsum) {this.id id;this.name name;this.bsum bsum;}public int getId() {return id;}public void set…

2024上海初中生古诗文大会倒计时4个多月:真题示例和独家解析

现在距离2024年初中生古诗文大会还有4个多月时间&#xff0c;我们继续来看10道选择题真题和详细解析&#xff0c;以下题目截取自我独家制作的在线真题集&#xff0c;都是来自于历届真题&#xff0c;去重、合并后&#xff0c;每道题都有参考答案和解析。 为帮助孩子自测和练习&…

Midjourney 平替 Leonardo AI 国内版上线关键还免费

Leonardo AI 正式在国内上线&#xff0c;功能相对基础&#xff0c;计划在两周后推出新一轮的更新&#xff0c;届时将支持 Elements (Lora) 和一些新的 XL 模型&#xff0c;逐步会把国际服上的功能移植过来。 虽然界面是英文&#xff0c;但是不要慌&#xff0c;你可以在 fanbook…

Cloud Serpent

Cloud Serpent 风蛇&#xff0c;刷厄运北很好用的&#xff0c;不记得早前好像就是50级副本神庙带俯冲&#xff0c;加速&#xff0c;远距离攻击&#xff0c;这样就不容易被厄运北法师和恶魔的法术攻击打中&#xff0c;残废术&#xff0c;而减速&#xff0c;刷本拉怪超级好用 闪…

历史的加速度:智人何时会迎来下一个版本?人类的命运与挑战

在《人类简史》中&#xff0c;尤瓦尔赫拉利主要探讨了人类的过去和发展历程&#xff0c;重点关注的是智人&#xff08;Homo sapiens&#xff09;。在他的续作《未来简史》中&#xff0c;他进一步探讨了未来人类的发展&#xff0c;并引入了“神人”&#xff08;Homo deus&#x…

C++之迭代器分类与List容器的使用

目录 迭代器的分类 List容器 ​编辑 总结 在Vector容器中我们学习了迭代器&#xff0c;知道了迭代器的作用和使用方法&#xff0c;本期我们将进一步学习迭代器的概念以及list容器的使用。 迭代器的分类 以算法库中的两个算法为例&#xff1a; sort算法是用来排序的&#…

常用MQ消息中间件Kafka、ZeroMQ和RabbitMQ对比及RabbitMQ详解

1、概述 在现代的分布式系统和实时数据处理领域&#xff0c;消息中间件扮演着关键的角色&#xff0c;用于解决应用程序之间的通信和数据传递的挑战。在众多的消息中间件解决方案中&#xff0c;Kafka、ZeroMQ和RabbitMQ 是备受关注和广泛应用的代表性系统。它们各自具有独特的特…

O2OA(翱途) 开发平台之HTTP端口规划

O2OA(翱途) 开发平台[下称O2OA开发平台或者O2OA]采用相对灵活的系统架构&#xff0c;支持三种服务器运行的方式。本篇主要阐述合并服务运行独立服务运行代理端口运行三种服务器运行方式。 一、先决条件&#xff1a; 1、O2Server服务器正常运行&#xff0c;系统安装部署请参考文…

Java基于jjwt操作jwt

之前讲解了jwt的相关知识&#xff0c;有不了解的&#xff0c;可以查看相关的文章JWT简介-CSDN博客&#xff0c;本节不再介绍&#xff0c;主要讲解有关java中如何通过jjwt库产生jwt以及解析jwt的相关操作。 添加maven依赖 <dependency><groupId>io.jsonwebtoken&l…

统信桌面操作系统上使用命令行添加软件图标到任务栏

原文链接&#xff1a;统信桌面操作系统上使用命令行添加软件图标到任务栏 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇在统信桌面操作系统上使用命令行添加软件图标到任务栏的文章。通过命令行将常用软件的图标添加到任务栏&#xff0c;可以快速启动软件&#xf…

网络抓包分析工具

摘要 随着网络技术的快速发展&#xff0c;网络数据的传输和处理变得日益复杂。网络抓包分析工具作为网络故障排查、性能优化以及安全审计的重要工具&#xff0c;对于提升网络管理的效率和准确性具有重要意义。本文旨在设计并实现一款高效、易用的网络抓包分析工具&#xff0c;…

Python实现数据库与Excel文件之间的数据导入与导出

数据库和Excel文件是两种常见且重要的数据存储方式。数据库通常用于大规模数据的高效存储、管理和查询&#xff0c;而Excel则以其直观的界面和简单的操作方式广泛应用于数据分析、报告生成和可视化等领域。在实际工作中&#xff0c;可能需要在这两者之间进行数据的导入与导出。…

网上零食销售系统

摘 要 随着互联网的快速发展&#xff0c;网上销售已成为零售业的重要组成部分。在众多的线上购物品类中&#xff0c;零食销售因其受众广泛、购买频率高、消费金额适中等特点&#xff0c;一直备受关注。然而&#xff0c;传统的零食销售方式&#xff0c;如实体店铺销售&#xff…

Python湍流隐式模型耗散粘性方程和大涡流模拟

&#x1f3af;要点 &#x1f3af;达朗贝尔一维波动通解&#xff0c;二维变速模拟 | &#x1f3af;达朗贝尔算子解双曲波形微分方程 | &#x1f3af;耗散系统粘性伯格斯方程快速傅里叶变换算法 | &#x1f3af;二维线性和非线性对流扩散解和湍流隐式建模 &#x1f4dc;偏微分方…

网络研究观:网络犯罪简报

通过犯罪研究人员精选的新闻提要了解最新的全球网络犯罪威胁。 了解不同的数字欺诈以及如何保护自己。 1. 网络犯罪分子冒充 CBI 和 IB 官员&#xff1a;KP 加尔各答警察局警告公民&#xff0c;诈骗者通过发送虚假的 CBI 和 IB 通知来勒索钱财&#xff0c;指控他们在线观看儿…

Avue框架学习

Avue框架学习 我们的项目使用的框架是 Avue 在我看来这个框架最大的特点是可以基于JSON配置页面上的From,Table以及各种各样的输入框等,不需要懂前端就可以很快上手,前提是需要多查一下文档 开发环境搭建 由于我本地的环境全是用docker来搭建的,所以我依然选择用docker搭建我…