引入
之前已经讲述了如何在 windows 环境通过 命令行的方式编译Java 代码。
此篇文章作为JVM 系列的引入,简要的说明如何在 windows 平台进行 Java 编译后的 class 文件的查看。其实 JDK 中已经内置了这些工具。可以在 %JAVA_HOME%\bin
目录下看到 javap.exe
这个可执行文件。一些其他的工具在该目录下也都能看到,不过不是此处重点,还是言归正传。本文只做基本演示,后面详细介绍字节码时会逐行详细分析。
示例代码
此处就举一个最简单不过的 Java 示例程序,代码如下:
public class Test{
private int m;
public int inc(){
return m + 1;
}
}
利器 javap
进入命令行,编译完源码后,会看到源码文件旁有个同名的 .class
文件。接着执行如下命令
javap -c Test (或 javap -c Test.class)
可以看到如下结果:
F:\tmp\jvmTmp>javap -c Test
Compiled from "Test.java"
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public int inc();
Code:
0: aload_0
1: getfield #2 // Field m:I
4: iconst_1
5: iadd
6: ireturn
}
这只是展示了当前类和方法的信息。如果要查看详细内容包括常量池、行号表、异常表等信息,可以通过如下命令查看:
javap -verbose Test
执行命令结果如下:
F:\tmp\jvmTmp>javap -verbose Test
Classfile /F:/tmp/jvmTmp/Test.class
Last modified 2018-7-18; size 265 bytes
MD5 checksum 0d5efc4b65ae7eb6d64f84136ce58ff9
Compiled from "Test.java"
public class Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#15 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#16 // Test.m:I
#3 = Class #17 // Test
#4 = Class #18 // java/lang/Object
#5 = Utf8 m
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 inc
#12 = Utf8 ()I
#13 = Utf8 SourceFile
#14 = Utf8 Test.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = NameAndType #5:#6 // m:I
#17 = Utf8 Test
#18 = Utf8 java/lang/Object
{
public Test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
public int inc();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: getfield #2 // Field m:I
4: iconst_1
5: iadd
6: ireturn
LineNumberTable:
line 6: 0
}
SourceFile: "Test.java"
可以看到多了文件路径、文件校验、上次修改时间、大小、版本信息、常量池等信息。这是展示字节码信息最丰富的命令了。class
文件中的信息几乎都展示出来了。其中第 11 行可以看到 Constant pool
就是常量池的意思,即行号 12 到 29 都是常量池部分。常量池中的常量通常以 #
开头接上序号。
#3 = Class #17 // Test
如上所示就是第三个常量,=
号后面是该常量项的类型,后面 #17
,表示指向第 17 个常量,这里不做展开。
更多用法
当然,javap
的用法不知这些,可以通过如下命令查看所有用法:
F:\tmp\jvmTmp>javap -help
用法: 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> 覆盖引导类文件的位置
大家可以都尝试下,加深印象。