使用Javap命令查看class文件的字节码

  • Post author:
  • Post category:java


今天有个同事问我如何查看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操作,这就是说明原始类型和包装类型比较的时候,将原始类型会自动转换成了包装类型后再比较,这也就是我们说的装箱过程