一、序言
就我个人而言,我想要搞清楚一个问题,就首先要明白以下几点:
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.csdn.net/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的反编译,关于编译的过程下篇文章讲解。