实践详解javap命令(反编译字节码)

1 缘起

最近在补充JVM相关知识,开始学着看Java字节码反编译结果,
对于目前的我而言,只是单纯地补充知识,没有实际的应用启发,
不过,还是要丰富自己的底层知识,
现整理学习过程的测试文档,分享如下,
帮助读者可以快速掌握反编译Java类,查看相关字节码的汇编信息。

2 javap

JDK8的javap官网文档地址:https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javap.html

2.1 简介

  • javap命令反汇编一个或多个class文件。输出依赖使用的参数,若不使用参数,直接使用javap命令会打印protected和public限定的类属性和方法。
  • javap命令不支持多版本jar。通过类路径形式使用javap命令只能查看当前jar文件的类文件反编译结果,通过URL形式使用javap命令只能查看指定版本类文件的反编译结果。
  • javap命令直接将结果打印到控制台。

2.2 查看javap参数

通过javap -help即可查看javap的参数,
Windows10中文平台会直接给出中文描述,
结果如下图所示。

javap -h

由图可知,JDK版本为1.8.0_291,支持invokedynamic,运行时解析调用方法。
实践详解javap命令(反编译字节码)_第1张图片

3 Code实战

3.1 原始类

这里先给出完整的原始类:ByteCodeTest.java,为后面生成字节码及反编译做准备,
其中,方法调用给出了5种,分别对应如下反编译标识:

  • 构造器方法(invokespecial)
  • 静态方法(invokestatic)
  • 接口方法(invokeinterface)
  • 虚方法(invokevirtual)
  • 动态方法(invokedynamic)
    在代码中在对应的位置已给出注释,源码如下:
package com.monkey.java_study.clzz;

import com.monkey.java_study.common.entity.UserEntity;
import com.monkey.java_study.proxy.jdk_proxy.IUserService;
import com.monkey.java_study.proxy.jdk_proxy.impl.UserServiceImpl;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 字节码反编译测试.
 *
 * @author xindaqi
 * @since 2022-08-11 10:48
 */
public class ByteCodeTest {

    public static final String TEST_NAME = "hello world!";

    private void test() {
        System.out.println(">>>>>>>>I am test method in current class");
    }

    public static void main(String[] args) {

        ByteCodeTest byteCodeTest = new ByteCodeTest();

        // invokespecial:调用当前类方法
        byteCodeTest.test();

        String var1 = "a";
        String var2 = "b";
        UserEntity userEntity = new UserEntity();

        // invokevirtual:调用其他类的方法
        userEntity.getNickname();

        IUserService userService = new UserServiceImpl();
        // invokeinterface
        userService.add();

        List<String> var4 = Stream.of("a", "b", "c").collect(Collectors.toList());

        // invokedynamic:运行时解析,调用方法,
        // invokeinterface:forEach为接口方法
        var4.forEach(s -> {
            if ("a".equals(s)) {
                // invokestatic:System中out为static方法
                System.out.println(">>>>>>>>I am " + s);
            }
        });
    }
}

3.2 编译为字节码

这里使用集成开发环境IDEA,通过build项目将ByteCodeTest.java编译为字节码,
操作方式如下图所示:
实践详解javap命令(反编译字节码)_第2张图片

存储在target文件夹下,具体的位置:D:/java-basic-with-maven/target/classes/com/monkey/java_study/clzz/ByteCodeTest.class
编译后项目目录树如下图所示:
实践详解javap命令(反编译字节码)_第3张图片

3.3 反编译

这里使用类文件路径方式反编译,即javap [options] class-file-path
其中,class-path-file为类文件路径。

3.3.1 javap -v

查看附加信息。

javap -v ByteCodeTest.class

结果如下图所示,简单的讲解,最前面的信息有:类文件大小、类文件的MD5、JDK版本等,
通过-v看到的信息比较完整,但是比较多,通过-c查看相关反编译指令。
实践详解javap命令(反编译字节码)_第4张图片

3.3.2 javap -l

输出行号和本地变量表。

javap -l ByteCodeTest.class

首先,可以看到原始代码的行号与字节码表的标识对应关系,如下图所示,
后面查看字节码序号时可以反查是哪一行的源码。
实践详解javap命令(反编译字节码)_第5张图片
本地变量签名结果如下图所示。
实践详解javap命令(反编译字节码)_第6张图片

3.3.3 javap -public

显示公共类和成员。

javap -public ByteCodeTest.class

在这里插入图片描述

3.3.4 javap -protected

显示受保护的/公共类和成员

javap -protected ByteCodeTest.class

在这里插入图片描述

3.3.5 javap -package

显示程序包/受保护的/公共类

javap -package ByteCodeTest.class

实践详解javap命令(反编译字节码)_第7张图片

3.3.6 javap -p

显示所有类和成员

javap -p ByteCodeTest.class

实践详解javap命令(反编译字节码)_第8张图片

3.3.7 javap -c

对代码进行反汇编。

javap -c ByteCodeTest.class

实践详解javap命令(反编译字节码)_第9张图片

3.3.8 javap -s

输出内部类型签名。

javap -s ByteCodeTest.class

实践详解javap命令(反编译字节码)_第10张图片

3.3.9 javap -sysinfo

显示正在处理类的系统信息(路径、大小、日期、MD5)

javap -sysinfo ByteCodeTest.class

实践详解javap命令(反编译字节码)_第11张图片

3.3.10 javap -constants

查看最终常量。

javap -constants ByteCodeTest.class

实践详解javap命令(反编译字节码)_第12张图片

4 小结

(1)javap命令反汇编一个或多个class文件。输出依赖使用的参数,若不使用参数,直接使用javap命令会打印protected和public限定的类属性和方法。
(2)javap命令不支持多版本jar。通过类路径形式使用javap命令只能查看当前jar文件的类文件反编译结果,通过URL形式使用javap命令只能查看指定版本类文件的反编译结果。
(3)javap命令直接将结果打印到控制台。
(4)常用查看汇编的命令:javap -c。

你可能感兴趣的:(#,Java,ABC,#,JVM,java,jvm,反编译,javap,字节码)