Skip to content

javac -g/javap 工具使用

javac 和 javap:字节码的编译与反编译

javacjavap 是 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.java

javap 反编译工具

基本用法

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.java

3. 查看基本信息

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: areturn

5. 查看详细字节码信息

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 文件结构」部分全部完成。接下来进入「字节码指令集」部分,首先看 指令集概述/分类/数据类型

基于 VitePress 构建