Javac是一个编译器,它的作用是将符合Java语言规范的源代码转换为符合Java虚拟机规范的Java字节码。 下面将详细讲解java中的编译和反编译。
基本概念
我们可以通过javac命令将Java程序的源代码编译为Java字节码,也就是我们常说的class文件,也就是我们一般理解的。 而且,字节码不是机器语言。 为了让机器执行它,需要将字节码翻译成机器指令。 这个过程是通过原语实现的,称为解释和执行。 注意:不要将编译与解释和执行混淆。 上面提到的前端编译过程是JVM为了提高效率而做出的优化。 在不同的虚拟机实现中,执行引擎执行字节码时,一般有解释执行(通过类库执行)和编译执行(通过即时编译器执行本地代码)两种选择,或者两者兼而有之。 那么你可以想一下,Java是编译型语言还是库语言? 那为什么java不直接编译成可执行文件呢,虽然主要目的是为了实现跨平台。 Java源代码被编译为字节码,然后由不同平台上的虚拟机解释执行,从而实现一次编译、到处运行的跨平台效果。
编译原理
Java语言的编译期分为两个阶段:后端编译和前端编译
后端编译
后端编译是指将*.java文件转换为*.class文件的过程,包括词法分析、语法分析、语义分析和中间代码生成。
主要有以下几个步骤:
前端编译
在一些商业虚拟机中,Java程序最初是通过类库解释和执行的。 当虚拟机发现某个方法或者代码块运行非常频繁时,就会将这段代码识别为热点代码。
为了提高热代码的执行效率,在运行时,虚拟机会将这段代码编译成与本地平台相关的机器码
完成这个任务的前端编译器称为即时编译器(JIT编译器)
反编译什么是反编译
由于Java编译是指将Java源代码编译为Java字节码的过程
所以Java反编译简单的说就是将Java字节码翻译成源代码的过程
为什么要反编译
首先,源代码是字符编码,字节码是二进制补码字节流,源代码是给人看的,字节码是给虚拟机看的
所以如果你想给别人看,你需要将字节码转换为源代码。如果你想为虚拟机执行,你需要将源代码编译为字节码。 当我们有了一个class文件,想要查看源代码时,我们可以使用反编译的方式来实现。
例如,如果你想知道某个Java语句糖编译后反编译成什么样子; 如果有人给你发了一个jar包,你需要看看里面某个类是怎么写的。 这种情况可以考虑使用Java反编译
反编译工具
京东GUI
GitHub:
官方网站:
Java编译与反编译 Java编译与反编译
下载后直接拖拽class文件或者jar包到界面
卢伊滕
下载链接:
阿尔萨斯
官方网站:
可以使用jad命令将JVM中运行的类的字节码反编译为java代码
爪哇
javap是jdk自带的一个工具,可以反编译代码,查看java编译器生成的字节码
直接通过javap-help查看其用法
用法: javap
其中, 可能的选项包括:
-help --help -? 输出此用法消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的
系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示最终常量
-classpath 指定查找用户类文件的位置
-cp 指定查找用户类文件的位置
-bootclasspath 覆盖引导类文件的位置
基本用途:
javac Test.java
javap -c Test.class
jclasslib
jclasslib是可视化字节码查看工具,可以直接安装在IDEA插件中
安装完成后,IDEA编译源码后,可以选择View”->”ShowBytecodeWithJclasslib查看字节码
可以直观的看到类文件包含基本信息、常量池、接口信息、字段信息、方法信息和属性信息
方法信息包括行号表、局部变量表、异常表等。
理解字节码指令涉及到很多知识c 反编译源码,接下来文章将通过案例详细讲解类文件结构以及字节码指令的执行过程
推荐两本特别经典的书:《深入理解Java虚拟机》、《Java虚拟机规范》
反编译示例
我们来看一个简单且常见的案例:
public class ForEachDemo {
public static void main(String[] args) {
List data = new ArrayList();
data.add("a");
data.add("b");
for (String str : data) {
System.out.println(str);
}
}
}
我们直接在IDEA中编译class文件,然后在目标目录中寻找该类,双击打开,得到反编译后的源码如下:
public class ForEachDemo {
public ForEachDemo() {
}
public static void main(String[] args) {
List data = new ArrayList();
data.add("a");
data.add("b");
Iterator var2 = data.iterator();
while(var2.hasNext()) {
String str = (String)var2.next();
System.out.println(str);
}
}
}
从上面的反编译代码可以清楚地看出,当原代码中没有已编译的构造方法时c 反编译源码,编译器会手动生成一个默认的构造方法; foreach循环遍历列表时,底层是通过iterator实现的