1.javap命令概述
如果直接查看Class的二进制文件,我们很难阅读,JDK提供了专门用来分析Class文件的工具javap以窥探Class文件的内部细节。
2. javap命令介绍
C:\Users\admin>javap
用法: javap <options> <classes>
其中, 可能的选项包括:
-help --help -? 输出此用法消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的
系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示最终常量
-classpath <path> 指定查找用户类文件的位置
-cp <path> 指定查找用户类文件的位置
-bootclasspath <path> 覆盖引导类文件的位置
3. javap解析字节码
3.1. 示例代码
package org.example;
public class Test {
private int number = 110;
public int add(){
number = number + 110;
return number;
}
}
3.2. javap解析出Class文件内容的理解
D:\IdeaProjects\MyDemo\target\classes\org\example>javap -v Test.class
Classfile /D:/IdeaProjects/MyDemo/target/classes/org/example/Test.class
Last modified 2023-6-9; size 390 bytes
MD5 checksum 6499e1baa710cce861d8f8113327109e
Compiled from "Test.java"
public class org.example.Test
minor version: 0 --副版本号
major version: 52 --主版本号 jdk1.8
flags: ACC_PUBLIC, ACC_SUPER --类访问和属性修饰符
Constant pool: --常量池
#1 = Methodref #4.#18 // java/lang/Object."<init>":()V -- 类种方法的符号引用
#2 = Fieldref #3.#19 // org/example/Test.number:I -- 字段的符号引用
#3 = Class #20 // org/example/Test -- 类或者接口的符号引用
#4 = Class #21 // java/lang/Object -- 类或者接口的符号引用
#5 = Utf8 number -- 字符串常量
#6 = Utf8 I -- 字符串常量
#7 = Utf8 <init> -- 字符串常量
#8 = Utf8 ()V -- 字符串常量
#9 = Utf8 Code -- 字符串常量
#10 = Utf8 LineNumberTable -- 字符串常量
#11 = Utf8 LocalVariableTable -- 字符串常量
#12 = Utf8 this -- 字符串常量
#13 = Utf8 Lorg/example/Test; -- 字符串常量
#14 = Utf8 add -- 字符串常量
#15 = Utf8 ()I -- 字符串常量
#16 = Utf8 SourceFile -- 字符串常量
#17 = Utf8 Test.java -- 字符串常量
#18 = NameAndType #7:#8 // "<init>":()V -- 字段或方法的符号引用
#19 = NameAndType #5:#6 // number:I -- 字段或方法的符号引用
#20 = Utf8 org/example/Test --字符串常量
#21 = Utf8 java/lang/Object --字符串常量
{ -- <init> 构造方法
public org.example.Test();
descriptor: ()V -- 构造方法返回值 void
flags: ACC_PUBLIC -- 构造方法访问权限 public
Code: -- Code属性结构,主要是描述代码指令相关
stack=2, locals=1, args_size=1 -- 参数数量
0: aload_0 -- 将引用类型局部变量表中下表为0的元素压入操作数栈
1: invokespecial #1 // Method java/lang/Object."<init>":()V -- 调用<init>方法
4: aload_0 -- 将引用类型局部变量表中下表为0的元素压入操作数栈
5: bipush 110 -- 将110压入操作数栈
7: putfield #2 // Field number:I -- 给堆中的类的实例对象赋值
10: return -- 返回
LineNumberTable: -- 字节码指令行号与源文件行号对应表
line 3: 0
line 4: 4
LocalVariableTable: -- 局部变量表 this存储在0下标
Start Length Slot Name Signature
0 11 0 this Lorg/example/Test;
public int add(); -- add 方法
descriptor: ()I -- 方法返回值 int
flags: ACC_PUBLIC -- 方法访问权限public
Code: -- Code属性结构,主要是描述代码指令相关
stack=3, locals=1, args_size=1 -- 最大栈深度3,局部变量表1,参数个数1
0: aload_0 -- 将引用类型局部变量表中下表为0的元素压入操作数栈
1: aload_0 -- 将引用类型局部变量表中下表为0的元素压入操作数栈
2: getfield #2 // Field number:I -- 访问堆中的实例对象值
5: bipush 110 -- 将110 压入操作数栈
7: iadd -- 栈顶2个元素相加
8: putfield #2 // Field number:I -- 给堆中的实例对象值
11: aload_0 -- 将引用类型局部变量表中下表为0的元素压入操作数栈
12: getfield #2 // Field number:I -- 访问堆中的实例对象值
15: ireturn -- 返回int类型数值
LineNumberTable: -- 字节码指令行号与源文件行号对应表
line 7: 0
line 8: 11
LocalVariableTable: -- 局部变量表 this存储在0下标
Start Length Slot Name Signature
0 16 0 this Lorg/example/Test;
}
SourceFile: "Test.java" -- 标识源文件的名称