Java编译和反编译那些事

简介: 挺久没更文章了,之前有一个月在面试,后来写了篇面经,有一些朋友找我交流问题,所以一直没时间写技术文章,估计以后更新文章频率不会那么高了,不过还是会定期分享的,我的目的还是希望我的每篇文章大家都能学到点东西基本概念我们可以通过javac命令将Java程序的源代码编译成Java字节码,即我们常说的class文件,这是我们通常意义上理解的编译但是,字节码并不是机器语言,要想让机器能够执行,还需要把字节码翻译成机器指令,这个过程是通过解释器实现的,叫解释执行在不同的虚拟机实现中,执行引擎在执行字节码的时候,通常会有解释执行(通过解释器执行)和编译执行(通过即时编译器产生本地代码执行)两种选择,也可能两

前言

挺久没更文章了,之前有一个月在面试,后来写了篇面经,有一些朋友找我交流问题,所以一直没时间写技术文章,估计以后更新文章频率不会那么高了,不过还是会定期分享的,我的目的还是希望我的每篇文章大家都能学到点东西


基本概念

我们可以通过javac命令将Java程序的源代码编译成Java字节码,即我们常说的class文件,这是我们通常意义上理解的编译

但是,字节码并不是机器语言,要想让机器能够执行,还需要把字节码翻译成机器指令,这个过程是通过解释器实现的,叫解释执行

注意:大家别把编译和解释执行混淆了,而后面所说的后端编译过程是JVM为提高效率做的优化

在不同的虚拟机实现中,执行引擎在执行字节码的时候,通常会有解释执行(通过解释器执行)和编译执行(通过即时编译器产生本地代码执行)两种选择,也可能两者兼备

所以大家可以思考下,Java到底是属于编译型语言还是解释器语言呢

那为什么java不直接编译成可执行文件呢

为了实现跨平台

Java源码通过编译成字节码,然后通过不同平台的虚拟机解释执行,从而实现 一次编译,到处运行的跨平台的效果


编译原理

Java语言的编译期分为前端编译和后端编译两个阶段

前端编译

前端编译是指把*.java文件转变成*.class文件的过程

包括词法分析、语法分析、语义分析与中间代码生成

主要有下面几个步骤:

后端编译

在部分商用虚拟机中,Java程序最初是通过解释器进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为热点代码

为了提高热点代码的执行效率,在运行时, 虚拟机将会把这些代码编译成与本地平台相关的机器码

完成这个任务的后端编译器称为即时编译器(JIT编译器)

之前写过一篇文章详细讲这个:聊聊JIT是如何影响JVM性能的


反编译

什么是反编译

既然Java 编译是指将 Java 源码编译成 Java 字节码的过程

那么Java 反编译简单说就是指根据 Java 字节码翻译成源码的过程

为什么要有反编译

首先这个源码是字符编码,字节码是二进制字节流,并且源码是给人看的,字节码是给虚拟机看的

因此如果想给人看,需要将字节码转为源码。如果想给虚拟机执行,需要将源码编译成字节码,当我们有类文件想看源码时,可以采用反编译的方式实现

比如想了解某个 Java 语法糖编译后,再反编译是什么样的;别人给你发一个 jar 包,你需要看其中某个类是怎么写的,等此类情况都可以考虑是用 Java 反编译

反编译工具

在线反编译工具

1.http://www.decompiler.com/

2.http://www.javadecompilers.com/,该网站的主要优势在于有多种反编译器可供选择

离线反编译工具

JD-GUI

GitHub :https://github.com/java-decompiler/jd-gui

官网:http://java-decompiler.github.io/


下载后将类文件或者 jar 包直接拖动到界面即可

Luyten

下载地址:https://github.com/deathmarine/Luyten/releases

Arthas

官网:https://arthas.aliyun.com/doc/

可以使用 jad 命令将 JVM 中运行的 class 的 byte code 反编译成 java 代码

这个工具很好用,强烈推荐

其他工具

javap

javap是jdk自带的一个工具,可以对代码反编译,也可以查看java编译器生成的字节码

直接通过javap -help查看其用法

用法: 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>    覆盖引导类文件的位置

基本使用:

javac Test.java
javap -c Test.class

jclasslib

jclasslib 是一种可视化的字节码查看工具,可以直接在 IDEA 插件安装

安装以后,在 IDEA 编译源码后,可以选择 View” ->“Show Bytecode With Jclasslib即可查看字节码

可以直观地看到 class 文件包含基本信息、常量池、接口信息、字段信息、方法信息和属性信息

其中方法信息又包含行号表、局部变量表,异常表等

要读懂字节码指令涉及的知识很多,之后的文章会通过案例详细讲解class文件结构和字节码指令的执行过程

推荐两本非常经典的图书:《深入理解 Java 虚拟机》、《Java 虚拟机规范》

大家也可以通过 Oracle 的 Java 标准 网页里浏览和下载《Java 语言规范》、《Java 虚拟机规范》

我公众号后台回复"666"也可获取pdf版电子书籍

反编译示例

下面看一个简单和常见的案例:

public class ForEachDemo {
    public static void main(String[] args) {
        List<String> data = new ArrayList<>();
        data.add("a");
        data.add("b");
        for (String str : data) {
            System.out.println(str);
        }
    }
}

我们直接在 IDEA 对该类文件进行编译,然后再 target 目录中寻找该类,双击打开,得到下面的反编译源码:

public class ForEachDemo {
    public ForEachDemo() {
    }
    public static void main(String[] args) {
        List<String> 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);
        }
    }
}

从上述反编译代码可以清楚地看到,原始代码中没有编写构造方法时,编译器会自动生成一个默认构造方法;foreach 循环来遍历 list 时,底层通过 iterator 来实现


最后

微信搜索:月伴飞鱼,交个朋友

公众号后台回复666,获得免费电子书籍,会持续更新

相关文章
|
3月前
|
Java Linux
java基础(3)安装好JDK后使用javac.exe编译java文件、java.exe运行编译好的类
本文介绍了如何在安装JDK后使用`javac.exe`编译Java文件,以及使用`java.exe`运行编译好的类文件。涵盖了JDK的安装、环境变量配置、编写Java程序、使用命令行编译和运行程序的步骤,并提供了解决中文乱码的方法。
73 2
|
2月前
|
分布式计算 大数据 Java
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
32 1
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
|
2月前
|
IDE Java 编译器
Java:如何确定编译和运行时类路径是否一致
类路径(Classpath)是JVM用于查找类文件的路径列表,对编译和运行Java程序至关重要。编译时通过`javac -classpath`指定,运行时通过`java -classpath`指定。IDE如Eclipse和IntelliJ IDEA也提供界面管理类路径。确保编译和运行时类路径一致,特别是外部库和项目内部类的路径设置。
143 5
|
2月前
|
Java Maven 数据安全/隐私保护
如何实现Java打包程序的加密代码混淆,避免被反编译?
【10月更文挑战第15天】如何实现Java打包程序的加密代码混淆,避免被反编译?
91 2
|
2月前
|
小程序 Oracle Java
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
这篇文章是关于JVM基础知识的介绍,包括JVM的跨平台和跨语言特性、Class文件格式的详细解析,以及如何使用javap和jclasslib工具来分析Class文件。
53 0
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
|
3月前
|
IDE Java 编译器
lombok编译遇到“找不到符号的问题”
【9月更文挑战第18天】当使用 Lombok 遇到 “找不到符号” 的问题时,可能是由于 Lombok 未正确安装、编译器不支持、IDE 配置不当或项目构建工具配置错误。解决方法包括确认 Lombok 安装、编译器支持,配置 IDE 和检查构建工具配置。通过这些步骤通常可解决问题,若问题仍存在,建议检查项目配置和依赖,或查看日志获取更多信息。
1117 2
|
4月前
|
存储 安全 Java
深入探讨Java的分层编译
本文主要探讨Java虚拟机(JVM)中的分层编译(Tiered Compilation)机制及其对程序性能的影响。
|
4月前
|
Java Android开发
解决Android编译报错:Unable to make field private final java.lang.String java.io.File.path accessible
解决Android编译报错:Unable to make field private final java.lang.String java.io.File.path accessible
572 1
|
4月前
|
存储 Java 编译器
|
5月前
|
Java 测试技术 Maven
Java编译器注解运行和自动生成代码问题之在编译时需要设置-proc:none参数问题如何解决
Java编译器注解运行和自动生成代码问题之在编译时需要设置-proc:none参数问题如何解决