JVM命令之javap

一、序言

  就我个人而言,我想要搞清楚一个问题,就首先要明白以下几点:

      1、javap是什么

      2、用了javap有什么用为什么要用它(重点)

      3、学有所用,来个例子

   所以我想各位朋友们,如果一个问题对你来说没什么用处肯定不会深究它,只有足够的魅力才能够吸引你去学习它。下面我们开始按照以上三个方面来给大家解析javap

二、解析

   要想明白为什么使用反编译,首先要明白编译是什么,使用源语言程序变成目标程序的过程,编译的过程主要分为一下几个步骤:词法分析;语法分析;语义检查和中间代码生成;代码优化;目标代码生成。主要是进行词法分析和语法分析,又称为源程序分析,分析过程中发现有语法错误,给出提示信息。

   就以java为例。我们编写的是*.java文件,编译之后会便成为*.class文件,很明显的是*.java文件的代码我们是可以完全看得懂的,如果想要一个java文件能够运行在不同的机器上,是要经历很多的,首先第一步就是将java文件编译为class文件,class文件中的内容我们是看不懂的,都是一些二进制文件,然后通过我们的JVM解释(编译)成为不同的机器上面的机器码,其实class文件虽然是二进制文件,但是并不是我们系统可以识别的机器指令,而是JVM可以识别的指令,JVM有自己独立一套指令系统,所以class中的二进制指令就是JVM可以识别的。这就是为什么要进行编译,java为什么要变成class文件的原因。我们最常用的两个jdk命令就是javac、java,其中javac就是将java文件编译成为class文件。

  但是目前还是不了解为什么使用javap,不要急,马上就会讲解。我们想要了解java代码是怎么执行的,所以我们需要能动class文件,但是class文件又都是二进制,我们还真的看不懂,这时候jdk为我们提供了javap命令,它能够将class中反编译成为我们稍微能看懂的汇编语言,都是一些命令。可以帮助我们更好的理解java的运行机理,而不是像某些java反编译工具一样只是将class文件反编译成为java代码。

   给一下我在网上看到的反编译说法:计算机软件反向工程(Reverse engineering)也称为计算机软件还原工程,是指通过对他人软件的目标程序(可执行程序)进行“逆向分析、研究”工作,以推导出他人的软件产品所使用的思路、原理、结构、算法、处理过程、运行方法等设计要素,某些特定情况下可能推导出源代码。反编译作为自己开发软件时的参考,或者直接用于自己的软件产品中。说的很长,但是很专业,我是更喜欢自己的见解,就是将我们看不懂的低级语言转为我们认识的高级语言,和编译作用相反。

三、javap的使用

   

PS C:\Users\ThinkPad> javap
用法: 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>    覆盖引导类文件的位置

   一般常用有-c -l -v

javap -v classxx,不仅会输出行号、本地变量表信息、反编译汇编代码,还会输出当前类用到的常量池等信息。
javap -l 会输出行号和本地变量表信息。
javap -c 会对当前class字节码进行反编译生成汇编代码。

查看汇编代码时,需要知道里面的jvm指令,可以参考官方文档:
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html

上面都是英文,但是都是正统的,我是看不懂,所以就附加一个其他朋友写的链接,可以自行查看如果是分析class文件反编译文件:https://blog.youkuaiyun.com/zhangpan19910604/article/details/52254053

不说了,下面来个例子来详细解释一下:

四、例子

java代码


public class JavapTest {
	
	private final static String obj1 = "因为是final需要初始化";
	private final String obj2 = "同上";
	private static String obj;
	private String obj4;
	private Integer obj5;
	
	public void method1() {
		System.out.println("this is my first method....");
	}
	
	public void method2() {
		System.out.println("this is my second method....");
		return ;
	}
	
	public Integer method3() {
		System.out.println("this is my third method....");
		return 123;
	}
	
	public void method() {
		System.out.println("this is my forth method....");
		method1();
		method2();
		int result = method3();
		System.out.println("result = " + result);
	}
	
	public static void main(String[] args) {
		JavapTest test = new JavapTest();
		test.method();
	}
}

反编译命令

PS E:\eclipse\JVMDemo\bin> javap -c .\JavapTest.class > javap-c.txt

生成的文件:

解析反编译之后的内容

 

Compiled from "JavapTest.java"
public class JavapTest {
  public JavapTest();
    Code:
       0: aload_0														aload_0 表示对this的操作,在static 方法中,aload_0表示对方法的第一参数的操作。
       1: invokespecial #20                 // Method java/lang/Object."<init>":()V			调用超类构造方法、实例初始化方法、私有方法
       4: aload_0														aload_0 表示对this的操作,在static 方法中,aload_0表示对方法的第一参数的操作。
       5: ldc           #11                 // String 同上								将int、float或String型常量值从常量池中推送至栈顶
       7: putfield      #22                 // Field obj2:Ljava/lang/String;					为指定的类的实例域赋值
      10: return														当前方法返回void

  public void method1();
    Code:
       0: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;	获取指定类的静态域,并将其值压入栈顶
       3: ldc           #35                 // String this is my first method....				将int、float或String型常量值从常量池中推送至栈顶
       5: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V		调用实例方法
       8: return										当前方法返回void

  public void method2();
    Code:
       0: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #44                 // String this is my second method....
       5: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return

  public java.lang.Integer method3();
    Code:
       0: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #48                 // String this is my third method....
       5: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: bipush        123								将一个byte型常量值推送至栈顶
      10: invokestatic  #50                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;		调用静态方法
      13: areturn

  public void method();
    Code:
       0: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #57                 // String this is my forth method....
       5: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: aload_0
       9: invokevirtual #59                 // Method method1:()V
      12: aload_0
      13: invokevirtual #61                 // Method method2:()V
      16: aload_0
      17: invokevirtual #63                 // Method method3:()Ljava/lang/Integer;
      20: invokevirtual #65                 // Method java/lang/Integer.intValue:()I
      23: istore_1						将栈顶int型数值存入第二个局部变量
      24: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
      27: new           #69                 // class java/lang/StringBuilder
      30: dup
      31: ldc           #71                 // String result =
      33: invokespecial #73                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      36: iload_1							第二个int型局部变量进栈,从0开始计数
      37: invokevirtual #75                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      40: invokevirtual #79                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      43: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      46: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #1                  // class JavapTest
       3: dup
       4: invokespecial #87                 // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #88                 // Method method:()V
      12: return
}

 

   五、JVM相关内容比较多,要想将JVM搞清楚确实不容易,这篇文章主要是讲解java的反编译,关于编译的过程下篇文章讲解。

  

### Javap命令的功能与使用指南 `javap` 是 JDK 提供的一款反汇编工具,用于显示 `.class` 文件中的字节码信息。它可以帮助开发者深入了解 Java 编译后的代码结构以及 JVM 的执行过程。以下是关于 `javap` 命令的具体功能和使用方法: #### 1. 功能概述 `javap` 主要用于反汇编已编译的 `.class` 文件,展示其内部结构。它可以揭示类的字段、方法签名及其对应的字节码指令[^4]。 - **基本用途** 显示类的公共成员(包括字段和方法),并提供详细的字节码表示形式。 - **高级用途** 反汇编特定方法的字节码,分析编译器优化策略或研究复杂的语言特性(如泛型、Lambda 表达式等)。 --- #### 2. 参数详解 以下是常用的 `javap` 参数列表及其作用: | 参数 | 描述 | |--------------|----------------------------------------------------------------------| | `-c` | 显示每个方法的字节码指令。这是最常用的功能之一,适用于分析方法实现细节[^4]。 | | `-l` | 显示行号表和局部变量表。这有助于调试和理解代码的上下文信息。 | | `-p` | 显示私有成员(包括私有字段和方法)。 | | `-s` | 显示字段和方法的内部类型描述符。 | | `-sysinfo` | 显示目标文件的系统信息(如访问标志、常量池大小等)。 | | `-constants` | 显示静态 final 字段的值。 | --- #### 3. 使用示例 以下是一些常见的 `javap` 使用场景及其实现方式: ##### 示例 1:查看简单类的字节码 假设有一个名为 `Example.java` 的文件,内容如下: ```java public class Example { public static void main(String[] args) { System.out.println("Hello, World!"); } } ``` 编译该文件后生成 `Example.class`,可以通过以下命令查看其字节码: ```bash javap -c Example ``` 输出可能类似于以下内容: ```plaintext Compiled from "Example.java" public class Example { public Example(); 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 #7 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #13 // String Hello, World! 5: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return } ``` ##### 示例 2:查看带 Lambda 表达式的字节码 如果存在一个带有 Lambda 表达式的类: ```java import java.util.function.Consumer; public class LambdaExample { public static void main(String[] args) { Consumer<String> consumer = s -> System.out.println(s); consumer.accept("Hello"); } } ``` 编译后运行以下命令: ```bash javap -c -v LambdaExample ``` 可以观察到 Lambda 表达式被转换为静态方法调用的过程。 --- #### 4. 应用场景 `javap` 工具广泛应用于以下几个方面: - **学习与教学** 利用 `javap` 辅助教学,帮助初学者直观理解 Java 内存模型和执行流程。 - **性能优化** 对比源代码与反编译结果,了解编译器如何优化代码。 - **错误排查** 在遇到难以理解的运行时错误时,通过查看字节码定位问题所在。 - **研究高级特性** 探索 Java 中的高级特性(如内部类、异常处理等)如何映射到字节码。 --- #### 5. 注意事项 - 避免对包含敏感数据的应用程序进行公开的字节码分析,以免泄露隐私信息[^4]。 - 如果需要更深层次的反编译支持,可考虑其他专用工具(如 JD-GUI 或 FernFlower)。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值