1. 概述
字节码分析是Java开发中常见的操作,用途广泛,比如排查代码问题、性能调优、查找带有特定注解的类等。
本文将系统介绍几种在Java中查看类文件字节码的方法,从命令行工具到第三方库,再到IDE插件,覆盖日常开发中的主要使用场景。
2. 什么是字节码?
字节码(Bytecode)是Java程序的中间表示形式,JVM通过它将Java代码翻译成机器级的汇编指令。
当Java源码被编译后,会生成 .class
文件,其中就包含了字节码。这些指令本身不可直接执行,必须依赖JVM进行解释或即时编译(JIT)。
✅ 字节码是平台无关的,这也是Java“一次编写,到处运行”的基础。
⚠️ 直接阅读字节码对排查某些底层问题(如泛型擦除、自动装箱、lambda实现)非常有帮助。
3. 使用 javap
命令
javap
是JDK自带的反编译工具,能查看类的字段、构造方法、方法签名,甚至反汇编出字节码指令。
3.1 基础用法:javap
查看 Object
类的基本结构:
$ javap java.lang.Object
输出:
public class java.lang.Object {
public java.lang.Object();
public final native java.lang.Class<?> getClass();
public native int hashCode();
public boolean equals(java.lang.Object);
protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException;
public java.lang.String toString();
public final native void notify();
public final native void notifyAll();
public final native void wait(long) throws java.lang.InterruptedException;
public final void wait(long, int) throws java.lang.InterruptedException;
public final void wait() throws java.lang.InterruptedException;
protected void finalize() throws java.lang.Throwable;
static {};
}
⚠️ 默认情况下,javap
不会显示 private
成员,这对分析某些类可能不够用。
3.2 查看所有成员:javap -p
加上 -p
参数可显示所有访问级别的成员:
$ javap -p java.lang.Object
输出中会多出 private
方法:
private static native void registerNatives();
✅ 这个参数在分析JDK源码或第三方库时非常实用,能看清完整结构。
3.3 详细信息:javap -v
使用 -v
参数可输出更详细的字节码信息,包括常量池、栈大小、方法描述符等:
Classfile jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/rt.jar!/java/lang/Object.class
Last modified Mar 15, 2017; size 1497 bytes
MD5 checksum 5916745820b5eb3e5647da3b6cc6ef65
Compiled from "Object.java"
public class java.lang.Object
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class #49 // java/lang/StringBuilder
// ...
{
public java.lang.Object();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 37: 0
public final native java.lang.Class<?> getClass();
descriptor: ()Ljava/lang/Class;
flags: ACC_PUBLIC, ACC_FINAL, ACC_NATIVE
Signature: #26 // ()Ljava/lang/Class<*>;
// ...
}
SourceFile: "Object.java"
✅ 这里能看到 major version 52
对应 JDK 8,对排查版本兼容问题很有帮助。
3.4 反汇编字节码:javap -c
使用 -c
参数可反汇编方法体,查看具体的字节码指令:
$ javap -c java.lang.Object
输出示例:
Compiled from "Object.java"
public class java.lang.Object {
public java.lang.Object();
Code:
0: return
public boolean equals(java.lang.Object);
Code:
0: aload_0
1: aload_1
2: if_acmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10:ireturn
protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException;
// ...
}
✅ aload_0
、if_acmpne
这些就是JVM的字节码指令,理解它们有助于深入理解Java底层机制。
📌 小贴士:运行 javap -help
可查看所有支持的参数。
4. 使用 ASM 库
ASM 是一个高性能、低层级的字节码操作和分析框架,广泛用于AOP、热更新、代码生成等场景。
4.1 引入依赖
在 pom.xml
中添加 ASM 核心和工具包依赖:
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.4</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<version>9.4</version>
</dependency>
4.2 查看字节码
使用 ClassReader
和 TraceClassVisitor
输出 Object
类的字节码:
try {
ClassReader reader = new ClassReader("java.lang.Object");
TraceClassVisitor tcv = new TraceClassVisitor(new PrintWriter(System.out));
reader.accept(tcv, 0);
} catch (IOException e) {
e.printStackTrace();
}
输出示例:
// class version 52.0 (52)
// access flags 0x21
public class java/lang/Object {
// compiled from: Object.java
// access flags 0x1
public <init>()V
L0
LINENUMBER 37 L0
RETURN
MAXSTACK = 0
MAXLOCALS = 1
// access flags 0x101
public native hashCode()I
// access flags 0x1
public equals(Ljava/lang/Object;)Z
L0
LINENUMBER 149 L0
ALOAD 0
ALOAD 1
IF_ACMPNE L1
ICONST_1
GOTO L2
L1
// ...
}
✅ ASM 输出格式清晰,适合程序化分析。
⚠️ 学习成本较高,适合需要深度操作字节码的场景。
5. 使用 BCEL
Apache Commons BCEL(Byte Code Engineering Library)提供了操作Java类文件的便捷方式,适合创建或修改类文件。
5.1 Maven 依赖
<dependency>
<groupId>org.apache.bcel</groupId>
<artifactId>bcel</artifactId>
<version>6.5.0</version>
</dependency>
5.2 反汇编并查看字节码
使用 Repository
获取 JavaClass
对象:
try {
JavaClass objectClazz = Repository.lookupClass("java.lang.Object");
System.out.println(objectClazz.toString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
输出示例:
public class java.lang.Object
file name java.lang.Object
compiled from Object.java
compiler version 52.0
access flags 33
constant pool 78 entries
ACC_SUPER flag true
Attribute(s):
SourceFile: Object.java
14 methods:
public void <init>()
private static native void registerNatives()
public final native Class getClass() [Signature: ()Ljava/lang/Class<*>;]
public native int hashCode()
public boolean equals(Object arg1)
protected native Object clone()
throws Exceptions: java.lang.CloneNotSupportedException
public String toString()
public final native void notify()
// ...
✅ JavaClass
提供了 getMethods()
、getFields()
、getConstantPool()
等方法,便于程序化访问类结构。
6. 使用 Javassist
Javassist(Java Programming Assistant)提供了更高层的API,相比ASM更易用,适合快速实现字节码操作。
6.1 Maven 依赖
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
6.2 生成并查看 ClassFile
使用 ClassPool
和 ClassFile
获取类信息:
try {
ClassPool cp = ClassPool.getDefault();
ClassFile cf = cp.get("java.lang.Object").getClassFile();
cf.write(new DataOutputStream(new FileOutputStream("Object.class")));
} catch (NotFoundException e) {
e.printStackTrace();
}
输出示例:
// Compiled from Object.java (version 1.8 : 52.0, super bit)
public class java.lang.Object {
// Method descriptor #19 ()V
// Stack: 0, Locals: 1
public Object();
0 return
Line numbers:
[pc: 0, line: 37]
// Method descriptor #19 ()V
private static native void registerNatives();
// Method descriptor #24 ()Ljava/lang/Class;
// Signature: ()Ljava/lang/Class<*>;
public final native java.lang.Class getClass();
// Method descriptor #28 ()I
public native int hashCode();
// ...
✅ Javassist API 更贴近Java语法,学习成本低。
✅ ClassFile
对象可直接访问方法、字段、常量池等结构。
7. 使用 Jclasslib 插件
对于日常开发,使用IDE插件查看字节码是最直观的方式。以IntelliJ IDEA为例,jclasslib Bytecode Viewer
是一个非常实用的插件。
7.1 安装插件
进入 Settings → Plugins → Marketplace,搜索 jclasslib
并安装:
7.2 查看 Object 类字节码
在编辑器中打开 Object.java
,选择菜单 View → Show Bytecode With Jclasslib:
插件会弹出字节码查看窗口:
7.3 查看详细信息
插件支持查看常量池、字段、方法等详细信息,界面清晰,适合快速分析:
✅ 开发中推荐使用此插件,效率远高于命令行。
📌 Eclipse 用户可使用 Bytecode Visualizer Plugin
实现类似功能。
8. 总结
本文介绍了多种查看Java类字节码的方式:
- ✅
javap
:JDK自带,简单粗暴,适合快速查看 - ✅ ASM:高性能,底层操作,适合框架开发
- ✅ BCEL:功能完整,API清晰,适合类文件工程化处理
- ✅ Javassist:高层API,易上手,适合快速实现字节码增强
- ✅ Jclasslib:IDE集成,可视化,日常开发首选
选择哪种方式取决于你的具体需求。如果是排查问题,推荐先用 javap -c
或 Jclasslib 插件;如果是做框架或中间件开发,ASM 或 Javassist 更合适。
所有示例代码已上传至 GitHub:https://github.com/yourname/java-bytecode-examples