手记

Java 反编译指南

1. 前言

在正式解读《Effective Java》之前,我们需要先了解 Java 反编译,因为反编译是我们学习和研究问题的重要手段之一。

结合反编译才能更好地理解《Effective Java》一书中给出的一些建议的根本原因。

贯穿整个专栏的大多数章节会涉及到 Java 反编译,因此能够灵活掌握反汇编对我们专栏后续的学习有极大的帮助。

本文将从反编译的工具,反编译举例等角度来讲解。

2. 是什么

虽然本节概念不难,但是仍然希望大家研究问题之前,一定先搞清楚概念。


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

Java 反编译是指根据 Java 字节码 “翻译” 成源码的过程。

3. 为什么

3.1 为什么要有反编译和反汇编呢?

为了深刻理解这个问题,大家可以思考他们的区别和目的是什么。

从编码来看,源码是字符编码,字节码是二进制字节流。

从目的来讲,源码是给人看的,字节码是给虚拟机看的。

因此如果想给人看,需要将字节码转为源码。如果想给虚拟机执行,需要将源码编译成字节码。

因此当我们有类文件想看源码时,可以采用反编译的方式实现。

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

3.2 为什么不直接编译成可执行文件?

不知道大家有没有思考过这个问题:为什么不直接编译成目标系统的可执行文件呢?

还记得 Java 设计的初衷吗?

对,就是跨平台。

如何实现跨平台的呢?

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

这体现出怎样的思想呢?

剑桥大学计算机科学家和计算机科学教授 David John Wheeler 有一句名言:

“Any problem in computer science can be solved by anther layer of indirection.”(计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决)

很多技术都是借鉴了这种思想:


如上图所示:

  • 为了加快指令的复制和执行效率,在处理器和主存之间加入高速缓存存储器。

  • 为了加快数据的访问速度,在数据库和应用程序之间加入缓存层(如 redis)。

  • 为了实现 Redis 的高可用,加入了哨兵对 redis 服务器进行监控。

  • 为了实现负载均衡,提高服务能力,在访问者和 Web 服务器之间,加入反向代理服务器。

  • 为了实现业务的快速搭建,实现能力的复用,产生了中台架构。

  • 为了实现削峰、解耦等,在服务之间加入消息队列。

  • 为了协调服务的调用者和提供者之间的关系,中间加入了注册中心。

  • 为了实现分布式事务,两阶段提交、三阶段提交方案中使用协调者作为中间角色来协调参与者的事务逻辑。

还有很多采用这种经典思想的案例,大家可以自行思考。

4. 怎么做

4.1 Java 反编译工具

反编译工具有两类,一类是在线反编译,一类是离线反编译工具。

4.1.1 在线反编译工具

在线反编译工具的优势在于,本地不需要安装任何软件,使用简单方便。

主要缺点是重要的非开源的公司项目的类文件或者 jar 包等通过使用第三方在线反编译平台有安全风险。

下面推荐两个不错的 Java 在线反编译网站。

第一个:http://www.decompiler.com/

第二个:http://www.javadecompilers.com/

该网站的主要优势在于有多种反编译器可供选择,大家也可以去对比这些 Java 反编译器的异同。

4.1.2 离线反编译工具

离线反编译工具的主要优势是安全性高,使用的体验更好。

同样推荐三款主流的离线反编译工具:IDEA 自带、 JD-GUI 、Luyten、CFR、 JAD 等。

一、IDEA 自带的反编译工具,大家在 IDEA 中点击类文件即可使用。

二、JD-GUI
JD-GUI 是一个知名的反编译工具。 使用非常方便,下载后将类文件或者 jar 包直接拖动到界面即可。

三、Luyten

Luyten 是另外一个不错的反编译工具。

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

四、arthas 反编译

如果我们想通过反编译查看服务器上运行的代码是不是正确地,该怎么办呢?

有一个更好的工具:arthas

可以使用 jad 命令将 JVM 中运行的 class 的 byte code 反编译成 java 代码,便于理解业务。

具体用法,大家可以看官方文档,并启动本地或者服务器项目去练习。

arthas 还提供了更多强大功能,请自行了解。

4.2 反编译示例

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

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 来实现。

5. 总结

本文主要讲述了 Java 反编译的概念、目的、本质,反编译的工具和案例等。希望大家能够在学习和研究问题时多尝试使用反编译。

下一节我们将讲述反汇编相关知识,讲述反汇编的目的、本质,反汇编的方法和案例等。

6. 思考与练习

1、 请自己动手分别使用在线反编译工具和本地反编译工具对示例代码编译后的类文件进行反编译。

2、你还了解哪些经典的方案是通过添加中间层来实现的,欢迎评论补充。

欢迎在下方的留言(评论)区给出你的答案。

---------------------------

本文选自 再学经典:《Effective Java》独家解析


想了解更多开发和避坑技巧,经验,学习方法少走弯路,

欢迎关注本人的慕课专栏:

再学经典:《Effective Java》独家解析

解锁大厂思维:剖析《阿里巴巴 Java 开发手册》




3人推荐
随时随地看视频
慕课网APP