You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用ASM修改Java类常量池中的静态final字符串?

修改Java类常量池的ASM实现方案

没问题,我来帮你搞定这个ASM修改常量池的任务~首先咱们得明确:虽然a是static final字符串,但因为它是编译期常量(直接赋值字面量),JVM会把代码中对它的引用直接替换成常量池中的字符串实例。不过咱们可以通过ASM拦截方法中的常量加载指令,或者直接修改类文件的常量池条目来实现替换,这里给你提供最直接可靠的实现方式。

核心思路

针对你的需求,咱们不需要复杂地遍历修改整个常量池,只需要在main方法中拦截加载旧字符串的ldc指令,将其替换为加载新字符串的指令即可——这种方式简单高效,且不会影响其他常量池条目。

完整的ExampleAgent代码

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class ExampleAgent implements ClassFileTransformer {
    // 目标类的全限定名(默认包下直接写类名,带包用斜杠分隔,比如com/test/Test)
    private static final String TRANSFORM_CLASS = "test";
    // 要替换的旧字符串常量
    private static final String OLD_TARGET_STRING = "Hello World";
    // 替换后的新字符串
    private static final String NEW_TARGET_STRING = "Multiply Of x*y is: ";

    public static void premain(String arg, Instrumentation instrumentation) {
        instrumentation.addTransformer(new ExampleAgent());
    }

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                            ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        // 只处理目标类,其他类直接返回原字节码
        if (!TRANSFORM_CLASS.equals(className)) {
            return null;
        }

        // 读取原类字节码
        ClassReader cr = new ClassReader(classfileBuffer);
        // 使用COMPUTE_FRAMES让ASM自动计算栈帧,避免手动处理复杂的栈帧逻辑
        ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
        
        cr.accept(new ClassVisitor(Opcodes.ASM9, cw) {
            @Override
            public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
                MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
                // 只拦截main方法
                if ("main".equals(name) && "([Ljava/lang/String;)V".equals(desc)) {
                    return new MethodVisitor(Opcodes.ASM9, mv) {
                        @Override
                        public void visitLdcInsn(Object constant) {
                            // 当遇到加载旧字符串的ldc指令时,替换为新字符串
                            if (OLD_TARGET_STRING.equals(constant)) {
                                super.visitLdcInsn(NEW_TARGET_STRING);
                            } else {
                                // 其他常量保持不变
                                super.visitLdcInsn(constant);
                            }
                        }
                    };
                }
                return mv;
            }
        }, ClassReader.EXPAND_FRAMES);

        return cw.toByteArray();
    }
}

关键细节说明

  1. 修正目标类名:你之前写的"src/test"是错误的,ASM中的className参数是类的全限定名(文件路径格式,不是源码路径),默认包下的test类直接写"test"即可。
  2. ASM版本选择:建议使用最新的ASM版本(比如ASM9),避免版本兼容问题,同时API更稳定。
  3. 自动计算栈帧:ClassWriter.COMPUTE_FRAMES参数让ASM自动处理方法的栈帧和局部变量表,省去了手动计算的繁琐步骤。
  4. 拦截ldc指令:visitLdcInsn方法会在遇到加载常量的指令时被调用,咱们只需要判断常量是否是要替换的旧字符串,替换成新字符串即可。

测试步骤

  1. 编译你的test.java类:javac test.java
  2. 编译ExampleAgent.java(需要把ASM的jar包加入classpath):javac -cp asm-9.5.jar ExampleAgent.java
  3. 使用JavaAgent运行测试类:java -javaagent:ExampleAgent.jar test
  4. 最终输出结果:
    Multiply Of x*y is: 
    250
    ASM is awasome
    

内容的提问来源于stack exchange,提问作者LEON

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:47:39