javac -g/javap 工具使用
javac 和 javap:字节码的编译与反编译
javac 和 javap 是 Java 字节码相关最常用的两个工具:
javac:把 Java 源码编译成字节码javap:把字节码反编译成人可读的格式
javac 编译选项
-g 选项:控制调试信息
bash
# 默认:只生成行号信息
javac User.java
# -g:none:生成任何调试信息(减小文件大小)
javac -g:none User.java
# -g:生成所有调试信息
javac -g User.java
# -g:source,vars:生成源码行号 + 局部变量信息
javac -g:source,vars User.java调试信息选项详解
| 选项 | 包含信息 |
|---|---|
source | 源文件名(SourceFile) |
lines | 行号表(LineNumberTable) |
vars | 局部变量表(LocalVariableTable) |
-parameters:保留方法参数名
bash
# 保留方法参数名(通过反射可访问)
javac -parameters User.java
# 查看效果
javap -v User.class | grep LocalVariableTable-proc:none:禁用注解处理
bash
# 跳过注解处理(加速编译)
javac -proc:none User.java-target:指定目标版本
bash
# 编译为 JDK 8 兼容的字节码(即使在 JDK 11 上编译)
javac -target 8 -source 8 User.java
# 这样编译出的 class 文件 major version = 52-source:指定源码版本
bash
# 使用 JDK 8 语法编译
javac -source 8 User.java-cp / -classpath:指定类路径
bash
javac -cp "lib/*:." User.javajavap 反编译工具
基本用法
bash
# 反编译查看公共成员
javap User.class
# 反编译查看完整信息
javap -v User.class
# 反编译只查看方法签名
javap -s User.class
# 反编译只查看 public 方法
javap -public User.class
# 反编译查看 private/protected 方法
javap -p User.class常用选项
| 选项 | 作用 |
|---|---|
-c | 反编译字节码指令 |
-v / -verbose | 完整详细信息 |
-s | 只打印内部类型描述符 |
-p | 显示 private/protected 成员 |
-public | 只显示 public 成员 |
-private | 显示所有成员 |
-l | 显示行号表和局部变量表 |
-sysinfo | 显示系统信息(class 文件路径、MD5、SHA1) |
-c:查看字节码指令
bash
javap -c User.class
# 输出:
# Compiled from "User.java"
# public class com.example.User {
# public com.example.User();
# Code:
# 0: aload_0
# 1: invokespecial #1 // Method java/lang/Object."<init>":()V
# 4: return
#
# public static void main(java.lang.String[]);
# Code:
# 0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
# 3: ldc #3 // String Hello
# 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
# 8: return
# }-v:查看详细信息
bash
javap -v User.class
# 包含:
# - Class 文件基本信息(版本、访问标识)
# - 常量池
# - 所有字段
# - 所有方法(带 Code 属性)
# - 所有子属性(LineNumberTable 等)-s:只查看描述符
bash
javap -s User.class
# 输出:
# public class com.example.User {
# public com.example.User();
# descriptor: ()V
#
# public static void main(java.lang.String[]);
# descriptor: ([Ljava/lang/String;)V
# }-l:显示行号表和局部变量表
bash
javap -l User.class
# 输出包含:
# LineNumberTable:
# line 5: 0
# line 6: 5
#
# LocalVariableTable:
# Start Length Slot Name Descriptor
# 0 10 0 this Lcom/example/User;-verbose:查看更详细信息
bash
javap -verbose User.class
# 包含额外信息:
# Classfile: /path/to/User.class
# MD5 checksum: ...
# Last modified: ...
# SHA-256: ...实战:完整分析一个 Class 文件
1. 编写源码
java
// User.java
public class User {
private String name;
private int age;
public User() { }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}2. 编译
bash
javac -g User.java3. 查看基本信息
bash
javap User.class
# 输出:
# public class User
# private java.lang.String name;
# private int age;
# public User();
# public java.lang.String getName();
# public void setName(java.lang.String);4. 查看字节码
bash
javap -c User.class
# 输出:
# public java.lang.String getName();
# Code:
# 0: aload_0
# 1: getfield #2 // Field name:Ljava/lang/String;
# 4: areturn5. 查看详细字节码信息
bash
javap -v User.class完整输出(部分):
Classfile /path/to/User.class
Last modified 2026-03-22; size 562 bytes
MD5 checksum ...
Compiled from "User.java"
public class User
minor version: 0
major version: 61 (JDK 17)
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #8.#21 // java/lang/Object."<init>":()V
#2 = Fieldref #7.#22 // User.name:Ljava/lang/String;
...
{
public User();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1
4: return
LineNumberTable:
line 3: 0
line 5: 4
}字节码指令的注释解读
javap -c 的输出中,每条指令后都有注释帮助理解:
bash
javap -c User.class
# 0: aload_0 ← 把局部变量槽 0(this)加载到栈
# 1: getfield #2 ← 访问字段 #2(name)
# 4: areturn ← 返回引用
# 0: getstatic #2 ← 获取静态字段 #2(System.out)
# 3: ldc #3 ← 从常量池加载常量 #3("Hello")
# 5: invokevirtual #4 ← 调用虚方法 #4(println)注释中的常量池引用
#1、#2 等引用的是常量池中的条目:
#1 = Methodref #8.#21 // java/lang/Object."<init>":()V
# └─ 指向类 #8,NameAndType #21本节小结
javac 和 javap 常用命令速查:
| 命令 | 作用 |
|---|---|
javac -g User.java | 编译并生成调试信息 |
javac -g:none User.java | 编译不生成调试信息 |
javac -parameters User.java | 保留方法参数名 |
javac -source 8 -target 8 | 编译为 JDK 8 字节码 |
javap User.class | 反编译查看公共成员 |
javap -c User.class | 反编译查看字节码指令 |
javap -v User.class | 反编译查看完整信息 |
javap -s User.class | 只查看描述符 |
javap -l User.class | 查看行号和局部变量表 |
javap -p User.class | 显示 private/protected |
-g:none 可以减小 class 文件大小(约 10%),适合生产环境部署;-parameters 对调试有帮助,适合开发环境。
到这里,「Class 文件结构」部分全部完成。接下来进入「字节码指令集」部分,首先看 指令集概述/分类/数据类型。
