今天有个同事问我如何查看class文件的字节码, 趁现在有时间写下来
首先什么是字节码?
Java中引入了虚拟机的概念,程序只需要面向虚拟机编程,编写好程序由解析器来编译,变异后成为机器可以读懂并且执行的代码。Java虚拟机可以读懂的代码我们称为Java 字节码,也就是java中的class文件。字节码是针对虚拟机而不是转对一种特定的机器, 只需要安装虚拟机就可以运行Java程序
class文件用Uedit 打开
什么是Javap
Javap是JDK自带的反汇编器,可以查看编译器为我们生成的字节码,通过它了解编译器的内公布工作
在控制台执行javap -help命令查看javap的命令信息
C:\Users\1000>javap -help
用法: javap <options> <classes>
其中, 可能的选项包括:
-? -h --help -help 输出此帮助消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的
系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示最终常量
--module <模块>, -m <模块> 指定包含要反汇编的类的模块
--module-path <路径> 指定查找应用程序模块的位置
--system <jdk> 指定查找系统模块的位置
--class-path <路径> 指定查找用户类文件的位置
-classpath <路径> 指定查找用户类文件的位置
-cp <路径> 指定查找用户类文件的位置
-bootclasspath <路径> 覆盖引导类文件的位置
Javap如何使用
这里有一个测试类Test.java
public class Test {
public static void main(String[] args) {
//创建原始类型
int a = 1;
//创建包装类型
Integer i1=new Integer(0);
Integer i2=new Integer(1);
//比较输出
print(i2.equals(a));
print(i1.equals(i2));
}
public static void print(Object o) {
System.out.println(o);
}
}
在Test.java文件所在目录下使用javac命令先编译java文件成class文件,执行命令后.java文件同名目录下会创建一个同名.class文件
备注:因为设置了JAVA_HOME所以可以执行javac等命令,如果没有设置JAVA_HOME那么会提示找不到命令
C:\Java>javac Test.java
然后使用javap命令查看字节码
C:\Java>javap -c Test
Compiled from "Test.java"
public class com.star.sms.dao.armgmt.jdbc.Test extends java.lang.Object{
public com.star.sms.dao.armgmt.jdbc.Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: new #2; //class java/lang/Integer
5: dup
6: iconst_0
7: invokespecial #3; //Method java/lang/Integer."<init>":(I)V
10: astore_2
11: new #2; //class java/lang/Integer
14: dup
15: iconst_1
16: invokespecial #3; //Method java/lang/Integer."<init>":(I)V
19: astore_3
20: aload_3
21: iload_1
22: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
25: invokevirtual #5; //Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
28: invokestatic #6; //Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
31: invokestatic #7; //Method print:(Ljava/lang/Object;)V
34: aload_2
35: aload_3
36: invokevirtual #5; //Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
39: invokestatic #6; //Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
42: invokestatic #7; //Method print:(Ljava/lang/Object;)V
45: return
public static void print(java.lang.Object);
Code:
0: getstatic #8; //Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokevirtual #9; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
7: return
}
我们发现25-31和36-42完全一样,22行做了Integer.valueOf操作,这就是说明原始类型和包装类型比较的时候,将原始类型会自动转换成了包装类型后再比较,这也就是我们说的装箱过程