未读代码 未读代码
首页
  • Java 18 新功能介绍
  • Java 17 新功能介绍
  • Java 16 新功能介绍
  • Java 15 新功能介绍
  • Java 14 新功能介绍
  • Java 8 新特性

    • Java 8 Lambda 表达式
    • Java 8 Stream 流式操作
    • Java 8 时间处理介绍
    • Java 8 Optional 介绍
  • Java 开发工具
Java 源码分析
Spring Boot 系列
  • Arthas 问题定位
  • JMH 基准测试
GitHub (opens new window)
首页
  • Java 18 新功能介绍
  • Java 17 新功能介绍
  • Java 16 新功能介绍
  • Java 15 新功能介绍
  • Java 14 新功能介绍
  • Java 8 新特性

    • Java 8 Lambda 表达式
    • Java 8 Stream 流式操作
    • Java 8 时间处理介绍
    • Java 8 Optional 介绍
  • Java 开发工具
Java 源码分析
Spring Boot 系列
  • Arthas 问题定位
  • JMH 基准测试
GitHub (opens new window)
  • Java 开发

  • Java 开发工具

    • Apache HttpClient 5 使用详细教程
    • Jackson 解析 JSON 详细教程
    • Guava - 拯救垃圾代码,写出优雅高效,效率提升N倍
    • 抛弃Eclipse,投入IDEA 的独孤求败江湖
    • Java 反编译工具的使用与对比分析
      • 前言
      • Procyon
        • 使用 Procyon
        • Procyon GUI
      • CFR​
        • CFR 命令行使用
        • CFR 代码中使用
      • JD-Core
        • 使用 JD-core
        • JD-GUI
      • Jadx
      • Fernflower
      • 反编译速度
      • 语法支持和可读性
        • Procyon
        • CFR
        • JD-Core
        • Jadx
        • Fernflower
      • 总结
    • 如何使用 Lombok 进行优雅的编码
    • 使用MyBatis Generator自动生成Model、Dao、Mapper相关代码
    • 使用Apache Ant 进行Java web项目打包并部署至TOMCAT
    • Linux配置Tomcat的单机多实例
  • 消息中间件

  • Java 开发
  • Java 开发工具
程序猿阿朗
2021-05-18

Java 反编译工具的使用与对比分析

# 前言

Java 反编译,一听可能觉得高深莫测,其实反编译并不是什么特别高级的操作,Java 对于 Class 字节码文件的生成有着严格的要求,如果你非常熟悉 Java 虚拟机规范,了解 Class 字节码文件中一些字节的作用,那么理解反编译的原理并不是什么问题。 甚至像下面这样的 Class 文件你都能看懂一二。

一般在逆向研究和代码分析中,反编译用到的比较多。不过在日常开发中,有时候只是简单的看一下所用依赖类的反编译,也是十分重要的。

恰好最近工作中也需要用到 Java 反编译,所以这篇文章介绍目前常见的的几种 Java 反编译工具的使用,在文章的最后也会通过编译速度、语法支持以及代码可读性三个维度,对它们进行测试,分析几款工具的优缺点。

# Procyon

Github 链接:https://github.com/mstrobel/procyon (opens new window) Procyon 不仅仅是反编译工具,它其实是专注于 Java 代码的生成和分析的一整套的 Java 元编程工具。 主要包括下面几个部分:

  • Core Framework
  • Reflection Framework
  • Expressions Framework
  • Compiler Toolset (Experimental)
  • Java Decompiler (Experimental)

可以看到反编译只是 Procyon 的其中一个模块,Procyon 原来托管于 bitbucket,后来迁移到了 GitHub,根据 GitHub 的提交记录来看,也有将近两年没有更新了。不过也有依赖 Procyon 的其他的开源反编译工具如** decompiler-procyon**,更新频率还是很高的,下面也会选择这个工具进行反编译测试。

# 使用 Procyon

<!-- https://mvnrepository.com/artifact/org.jboss.windup.decompiler/decompiler-procyon -->
<dependency>
    <groupId>org.jboss.windup.decompiler</groupId>
    <artifactId>decompiler-procyon</artifactId>
    <version>5.1.4.Final</version>
</dependency>
1
2
3
4
5
6

写一个简单的反编译测试。

package com.wdbyte.decompiler;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.List;

import org.jboss.windup.decompiler.api.DecompilationFailure;
import org.jboss.windup.decompiler.api.DecompilationListener;
import org.jboss.windup.decompiler.api.DecompilationResult;
import org.jboss.windup.decompiler.api.Decompiler;
import org.jboss.windup.decompiler.procyon.ProcyonDecompiler;

/**
 * Procyon 反编译测试
 *
 *  @author https://github.com/niumoo
 * @date 2021/05/15
 */
public class ProcyonTest {
    public static void main(String[] args) throws IOException {
        Long time = procyon("decompiler.jar", "procyon_output_jar");
        System.out.println(String.format("decompiler time: %dms", time));
    }
    public static Long procyon(String source,String targetPath) throws IOException {
        long start = System.currentTimeMillis();
        Path outDir = Paths.get(targetPath);
        Path archive = Paths.get(source);
        Decompiler dec = new ProcyonDecompiler();
        DecompilationResult res = dec.decompileArchive(archive, outDir, new DecompilationListener() {
            public void decompilationProcessComplete() {
                System.out.println("decompilationProcessComplete");
            }
            public void decompilationFailed(List<String> inputPath, String message) {
                System.out.println("decompilationFailed");
            }
            public void fileDecompiled(List<String> inputPath, String outputPath) {
            }
            public boolean isCancelled() {
                return false;
            }
        });

        if (!res.getFailures().isEmpty()) {
            StringBuilder sb = new StringBuilder();
            sb.append("Failed decompilation of " + res.getFailures().size() + " classes: ");
            Iterator failureIterator = res.getFailures().iterator();
            while (failureIterator.hasNext()) {
                DecompilationFailure dex = (DecompilationFailure)failureIterator.next();
                sb.append(System.lineSeparator() + "    ").append(dex.getMessage());
            }
            System.out.println(sb.toString());
        }
        System.out.println("Compilation results: " + res.getDecompiledFiles().size() + " succeeded, " + res.getFailures().size() + " failed.");
        dec.close();
        Long end = System.currentTimeMillis();
        return end - start;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

Procyon 在反编译时会实时输出反编译文件数量的进度情况,最后还会统计反编译成功和失败的 Class 文件数量。

....
五月 15, 2021 10:58:28 下午 org.jboss.windup.decompiler.procyon.ProcyonDecompiler$3 call
信息: Decompiling 650 / 783
五月 15, 2021 10:58:30 下午 org.jboss.windup.decompiler.procyon.ProcyonDecompiler$3 call
信息: Decompiling 700 / 783
五月 15, 2021 10:58:37 下午 org.jboss.windup.decompiler.procyon.ProcyonDecompiler$3 call
信息: Decompiling 750 / 783
decompilationProcessComplete
Compilation results: 783 succeeded, 0 failed.
decompiler time: 40599ms
1
2
3
4
5
6
7
8
9
10

# Procyon GUI

对于 Procyon 反编译来说,在 GitHub 上也有基于此实现的开源 GUI 界面,感兴趣的可以下载尝试。 Github 地址:https://github.com/deathmarine/Luyten (opens new window)

# CFR​

GitHub 地址:https://github.com/leibnitz27/cfr (opens new window) CFR 官方网站:http://www.benf.org/other/cfr/ (opens new window)(访问可能需要FQ) Maven 仓库: https://mvnrepository.com/artifact/org.benf/cfr (opens new window)

CFR(Class File Reader) 可以支持 Java 9、Java 12、Java 14 以及其他的最新版 Java 代码的反编译工作。而且 CFR 本身的代码是由 Java 6 编写,所以基本可以使用 CFR 在任何版本的 Java 程序中。值得一提的是,使用 CFR 甚至可以将使用其他语言编写的的 JVM 类文件反编译回 Java 文件。​

# CFR 命令行使用

使用 CFR 反编译时,你可以下载已经发布的 JAR 包,进行命令行反编译,也可以使用 Maven 引入的方式,在代码中使用。下面先说命令行运行的方式。

直接在 GitHub Tags 下载 (opens new window)已发布的最新版 JAR. 可以直接运行查看帮助。

# 查看帮助
java -jar cfr-0.151.jar --help
1
2

如果只是反编译某个 class.

# 反编译 class 文件,结果输出到控制台
java -jar cfr-0.151.jar WindupClasspathTypeLoader.class
# 反编译 class 文件,结果输出到 out 文件夹
java -jar cfr-0.151.jar WindupClasspathTypeLoader.class --outputpath ./out
1
2
3
4

反编译某个 JAR.

# 反编译 jar 文件,结果输出到 output_jar 文件夹
➜  Desktop java -jar cfr-0.151.jar decompiler.jar --outputdir ./output_jar
Processing decompiler.jar (use silent to silence)
Processing com.strobel.assembler.metadata.ArrayTypeLoader
Processing com.strobel.assembler.metadata.ParameterDefinition
Processing com.strobel.assembler.metadata.MethodHandle
Processing com.strobel.assembler.metadata.signatures.FloatSignature
.....
1
2
3
4
5
6
7
8

反编译结果会按照 class 的包路径写入到指定文件夹中。

# CFR 代码中使用

添加依赖这里不提。

<!-- https://mvnrepository.com/artifact/org.benf/cfr -->
<dependency>
    <groupId>org.benf</groupId>
    <artifactId>cfr</artifactId>
    <version>0.151</version>
</dependency>
1
2
3
4
5
6

实际上我在官方网站和 GitHub 上都没有看到具体的单元测试示例。不过没有关系,既然能在命令行运行,那么直接在 IDEA 中查看反编译后的 Main 方法入口,看下命令行是怎么执行的,就可以写出自己的单元测试了。

package com.wdbyte.decompiler;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.benf.cfr.reader.api.CfrDriver;
import org.benf.cfr.reader.util.getopt.OptionsImpl;

/**
 * CFR Test
 *
 * @author https://github.com/niumoo
 * @date 2021/05/15
 */
public class CFRTest {
    public static void main(String[] args) throws IOException {
        Long time = cfr("decompiler.jar", "./cfr_output_jar");
        System.out.println(String.format("decompiler time: %dms", time));
        // decompiler time: 11655ms
    }
    public static Long cfr(String source, String targetPath) throws IOException {
        Long start = System.currentTimeMillis();
        // source jar
        List<String> files = new ArrayList<>();
        files.add(source);
        // target dir
        HashMap<String, String> outputMap = new HashMap<>();
        outputMap.put("outputdir", targetPath);

        OptionsImpl options = new OptionsImpl(outputMap);
        CfrDriver cfrDriver = new CfrDriver.Builder().withBuiltOptions(options).build();
        cfrDriver.analyse(files);
        Long end = System.currentTimeMillis();
        return (end - start);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# JD-Core

GiHub 地址:https://github.com/java-decompiler/jd-core (opens new window) JD-core 官方网址:https://java-decompiler.github.io/ (opens new window) JD-core 是一个的独立的 Java 库,可以用于 Java 的反编译,支持从 Java 1 至 Java 12 的字节码反编译,包括 Lambda 表达式、方式引用、默认方法等。知名的 JD-GUI 和 Eclipse 无缝集成反编译引擎就是 JD-core。 JD-core 提供了一些反编译的核心功能,也提供了单独的 Class 反编译方法,但是如果你想在自己的代码中去直接反编译整个 JAR 包,还是需要一些改造的,如果是代码中有匿名函数,Lambda 等,虽然可以直接反编译,不过也需要额外考虑。

# 使用 JD-core

        <!-- https://mvnrepository.com/artifact/org.jd/jd-core -->
        <dependency>
            <groupId>org.jd</groupId>
            <artifactId>jd-core</artifactId>
            <version>1.1.3</version>
        </dependency>
1
2
3
4
5
6

为了可以反编译整个 JAR 包,使用的代码我做了一些简单改造,以便于最后一部分的对比测试,但是这个示例中没有考虑内部类,Lambda 等会编译出多个 Class 文件的情况,所以不能直接使用在生产中。

package com.wdbyte.decompiler;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.jd.core.v1.ClassFileToJavaSourceDecompiler;
import org.jd.core.v1.api.loader.Loader;
import org.jd.core.v1.api.printer.Printer;

/**
 * @author https://github.com/niumoo
 * @date 2021/05/15
 */
public class JDCoreTest {

    public static void main(String[] args) throws Exception {
        JDCoreDecompiler jdCoreDecompiler = new JDCoreDecompiler();
        Long time = jdCoreDecompiler.decompiler("decompiler.jar","jd_output_jar");
        System.out.println(String.format("decompiler time: %dms", time));
    }
}


class JDCoreDecompiler{

    private ClassFileToJavaSourceDecompiler decompiler = new ClassFileToJavaSourceDecompiler();
    // 存放字节码
    private HashMap<String,byte[]> classByteMap = new HashMap<>();

    /**
     * 注意:没有考虑一个 Java 类编译出多个 Class 文件的情况。
     * 
     * @param source
     * @param target
     * @return
     * @throws Exception
     */
    public Long decompiler(String source,String target) throws Exception {
        long start = System.currentTimeMillis();
        // 解压
        archive(source);
        for (String className : classByteMap.keySet()) {
            String path = StringUtils.substringBeforeLast(className, "/");
            String name = StringUtils.substringAfterLast(className, "/");
            if (StringUtils.contains(name, "$")) {
                name = StringUtils.substringAfterLast(name, "$");
            }
            name = StringUtils.replace(name, ".class", ".java");
            decompiler.decompile(loader, printer, className);
            String context = printer.toString();
            Path targetPath = Paths.get(target + "/" + path + "/" + name);
            if (!Files.exists(Paths.get(target + "/" + path))) {
                Files.createDirectories(Paths.get(target + "/" + path));
            }
            Files.deleteIfExists(targetPath);
            Files.createFile(targetPath);
            Files.write(targetPath, context.getBytes());
        }
        return System.currentTimeMillis() - start;
    }
    private void archive(String path) throws IOException {
        try (ZipFile archive = new JarFile(new File(path))) {
            Enumeration<? extends ZipEntry> entries = archive.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                if (!entry.isDirectory()) {
                    String name = entry.getName();
                    if (name.endsWith(".class")) {
                        byte[] bytes = null;
                        try (InputStream stream = archive.getInputStream(entry)) {
                            bytes = IOUtils.toByteArray(stream);
                        }
                        classByteMap.put(name, bytes);
                    }
                }
            }
        }
    }

    private Loader loader = new Loader() {
        @Override
        public byte[] load(String internalName) {
            return classByteMap.get(internalName);
        }
        @Override
        public boolean canLoad(String internalName) {
            return classByteMap.containsKey(internalName);
        }
    };

    private Printer printer = new Printer() {
        protected static final String TAB = "  ";
        protected static final String NEWLINE = "\n";
        protected int indentationCount = 0;
        protected StringBuilder sb = new StringBuilder();
        @Override public String toString() {
            String toString = sb.toString();
            sb = new StringBuilder();
            return toString;
        }
        @Override public void start(int maxLineNumber, int majorVersion, int minorVersion) {}
        @Override public void end() {}
        @Override public void printText(String text) { sb.append(text); }
        @Override public void printNumericConstant(String constant) { sb.append(constant); }
        @Override public void printStringConstant(String constant, String ownerInternalName) { sb.append(constant); }
        @Override public void printKeyword(String keyword) { sb.append(keyword); }
        @Override public void printDeclaration(int type, String internalTypeName, String name, String descriptor) { sb.append(name); }
        @Override public void printReference(int type, String internalTypeName, String name, String descriptor, String ownerInternalName) { sb.append(name); }
        @Override public void indent() { this.indentationCount++; }
        @Override public void unindent() { this.indentationCount--; }
        @Override public void startLine(int lineNumber) { for (int i=0; i<indentationCount; i++) sb.append(TAB); }
        @Override public void endLine() { sb.append(NEWLINE); }
        @Override public void extraLine(int count) { while (count-- > 0) sb.append(NEWLINE); }
        @Override public void startMarker(int type) {}
        @Override public void endMarker(int type) {}
    };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

# JD-GUI

GitHub 地址:https://github.com/java-decompiler/jd-gui (opens new window) JD-core 也提供了官方的 GUI 界面,需要的也可以直接下载尝试。

# Jadx

GitHub 地址:https://github.com/skylot/jadx (opens new window) Jadx 是一款可以反编译 JAR、APK、DEX、AAR、AAB、ZIP 文件的反编译工具,并且也配有 Jadx-gui 用于界面操作。 Jadx 使用 Grade 进行依赖管理,可以自行克隆仓库打包运行。

git clone https://github.com/skylot/jadx.git
cd jadx
./gradlew dist
# 查看帮助
 ./build/jadx/bin/jadx --help
 
jadx - dex to java decompiler, version: dev

usage: jadx [options] <input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab)
options:
  -d, --output-dir                    - output directory
  -ds, --output-dir-src               - output directory for sources
  -dr, --output-dir-res               - output directory for resources
  -r, --no-res                        - do not decode resources
  -s, --no-src                        - do not decompile source code
  --single-class                      - decompile a single class
  --output-format                     - can be 'java' or 'json', default: java
  -e, --export-gradle                 - save as android gradle project
  -j, --threads-count                 - processing threads count, default: 6
  --show-bad-code                     - show inconsistent code (incorrectly decompiled)
  --no-imports                        - disable use of imports, always write entire package name
  --no-debug-info                     - disable debug info
  --add-debug-lines                   - add comments with debug line numbers if available
  --no-inline-anonymous               - disable anonymous classes inline
  --no-replace-consts                 - don't replace constant value with matching constant field
  --escape-unicode                    - escape non latin characters in strings (with \u)
  --respect-bytecode-access-modifiers - don't change original access modifiers
  --deobf                             - activate deobfuscation
  --deobf-min                         - min length of name, renamed if shorter, default: 3
  --deobf-max                         - max length of name, renamed if longer, default: 64
  --deobf-cfg-file                    - deobfuscation map file, default: same dir and name as input file with '.jobf' extension
  --deobf-rewrite-cfg                 - force to save deobfuscation map
  --deobf-use-sourcename              - use source file name as class name alias
  --deobf-parse-kotlin-metadata       - parse kotlin metadata to class and package names
  --rename-flags                      - what to rename, comma-separated, 'case' for system case sensitivity, 'valid' for java identifiers, 'printable' characters, 'none' or 'all' (default)
  --fs-case-sensitive                 - treat filesystem as case sensitive, false by default
  --cfg                               - save methods control flow graph to dot file
  --raw-cfg                           - save methods control flow graph (use raw instructions)
  -f, --fallback                      - make simple dump (using goto instead of 'if', 'for', etc)
  -v, --verbose                       - verbose output (set --log-level to DEBUG)
  -q, --quiet                         - turn off output (set --log-level to QUIET)
  --log-level                         - set log level, values: QUIET, PROGRESS, ERROR, WARN, INFO, DEBUG, default: PROGRESS
  --version                           - print jadx version
  -h, --help                          - print this help
Example:
  jadx -d out classes.dex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

根据 HELP 信息,如果想要反编译 decompiler.jar 到 out 文件夹。

./build/jadx/bin/jadx -d ./out ~/Desktop/decompiler.jar 
INFO  - loading ...
INFO  - processing ...
INFO  - doneress: 1143 of 1217 (93%)
1
2
3
4

# Fernflower

GitHub 地址:https://github.com/fesh0r/fernflower (opens new window) Fernflower 和 Jadx 一样使用 Grade 进行依赖管理,可以自行克隆仓库打包运行。

➜  fernflower-master ./gradlew build

BUILD SUCCESSFUL in 32s
4 actionable tasks: 4 executed

➜  fernflower-master java -jar build/libs/fernflower.jar
Usage: java -jar fernflower.jar [-<option>=<value>]* [<source>]+ <destination>
Example: java -jar fernflower.jar -dgs=true c:\my\source\ c:\my.jar d:\decompiled\

➜  fernflower-master mkdir out
➜  fernflower-master java -jar build/libs/fernflower.jar ~/Desktop/decompiler.jar ./out
INFO:  Decompiling class com/strobel/assembler/metadata/ArrayTypeLoader
INFO:  ... done
INFO:  Decompiling class com/strobel/assembler/metadata/ParameterDefinition
INFO:  ... done
INFO:  Decompiling class com/strobel/assembler/metadata/MethodHandle
...

➜  fernflower-master ll out
total 1288
-rw-r--r--  1 darcy  staff   595K  5 16 17:47 decompiler.jar
➜  fernflower-master
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Fernflower 在反编译 JAR 包时,默认反编译的结果也是一个 JAR 包。Jad

# 反编译速度

到这里已经介绍了五款 Java 反编译工具了,那么在日常开发中我们应该使用哪一个呢?又或者在代码分析时我们又该选择哪一个呢?我想这两种情况的不同,使用时的关注点也是不同的。如果是日常使用,读读代码,我想应该是对可读性要求更高些,如果是大量的代码分析工作,那么可能反编译的速度和语法的支持上要求更高些。 为了能有一个简单的参考数据,我使用 JMH 微基准测试工具分别对这五款反编译工具进行了简单的测试,下面是一些测试结果。

测试环境

环境变量 描述
处理器 2.6 GHz 六核Intel Core i7
内存 16 GB 2667 MHz DDR4
Java 版本 JDK 14.0.2
测试方式 JMH 基准测试。
待反编译 JAR 1 procyon-compilertools-0.5.33.jar (1.5 MB)
待反编译 JAR 2 python2java4common-1.0.0-20180706.084921-1.jar (42 MB)

反编译 JAR 1:procyon-compilertools-0.5.33.jar (1.5 MB)

Benchmark Mode Cnt Score Units
cfr avgt 10 6548.642 ±  363.502 ms/op
fernflower avgt 10 12699.147 ± 1081.539 ms/op
jdcore avgt 10 5728.621 ±  310.645 ms/op
procyon avgt 10 26776.125 ± 2651.081 ms/op
jadx avgt 10 7059.354 ±  323.351 ms/op

反编译 JAR 2:  python2java4common-1.0.0-20180706.084921-1.jar (42 MB)

JAR 2 这个包是比较大的,是拿很多代码仓库合并到一起的,同时还有很多 Python 转 Java 生成的代码,理论上代码的复杂度会更高。

Benchmark Cnt Score
Cfr 1 413838.826ms
fernflower 1 246819.168ms
jdcore 1 Error
procyon 1 487647.181ms
jadx 1 505600.231ms

# 语法支持和可读性

如果反编译后的代码需要自己看的话,那么可读性更好的代码更占优势,下面我写了一些代码,主要是 Java 8 及以下的代码语法和一些嵌套的流程控制,看看反编译后的效果如何。

    package com.wdbyte.decompiler;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.stream.IntStream;
    
    import org.benf.cfr.reader.util.functors.UnaryFunction;
    
    /**
     * @author https://www.wdbyte.com
     * @date 2021/05/16
     */
    public class HardCode <A, B> {
        public HardCode(A a, B b) { }
    
        public static void test(int... args) { }
    
        public static void main(String... args) {
            test(1, 2, 3, 4, 5, 6);
        }
    
        int byteAnd0() {
            int b = 1;
            int x = 0;
            do {
                b = (byte)((b ^ x));
            } while (b++ < 10);
            return b;
        }
    
        private void a(Integer i) {
            a(i);
            b(i);
            c(i);
        }
    
        private void b(int i) {
            a(i);
            b(i);
            c(i);
        }
    
        private void c(double d) {
            c(d);
            d(d);
        }
    
        private void d(Double d) {
            c(d);
            d(d);
        }
    
        private void e(Short s) {
            b(s);
            c(s);
            e(s);
            f(s);
        }
    
        private void f(short s) {
            b(s);
            c(s);
            e(s);
            f(s);
        }
    
        void test1(String path) {
            try {
                int x = 3;
            } catch (NullPointerException t) {
                System.out.println("File Not found");
                if (path == null) { return; }
                throw t;
            } finally {
                System.out.println("Fred");
                if (path == null) { throw new IllegalStateException(); }
            }
        }
    
        private final List<Integer> stuff = new ArrayList<>();{
            stuff.add(1);
            stuff.add(2);
        }
    
        public static int plus(boolean t, int a, int b) {
            int c = t ? a : b;
            return c;
        }
    
        // Lambda
        Integer lambdaInvoker(int arg, UnaryFunction<Integer, Integer> fn) {
            return fn.invoke(arg);
        }
    
        // Lambda
        public int testLambda() {
            return lambdaInvoker(3, x -> x + 1);
            //        return 1;
        }
    
        // Lambda
        public Integer testLambda(List<Integer> stuff, int y, boolean b) {
            return stuff.stream().filter(b ? x -> x > y : x -> x < 3).findFirst().orElse(null);
        }
    
        // stream
        public static <Y extends Integer> void testStream(List<Y> list) {
            IntStream s = list.stream()
                .filter(x -> {
                    System.out.println(x);
                    return x.intValue() / 2 == 0;
                    })
                .map(x -> (Integer)x+2)
                .mapToInt(x -> x);
            s.toArray();
        }
    
        // switch
        public void testSwitch1(){
            int i = 0;
            switch(((Long)(i + 1L)) + "") {
                case "1":
                    System.out.println("one");
            }
        }
        // switch
        public void testSwitch2(String string){
            switch (string) {
                case "apples":
                    System.out.println("apples");
                    break;
                case "pears":
                    System.out.println("pears");
                    break;
            }
        }
    
        // switch
        public static void testSwitch3(int x) {
            while (true) {
                if (x < 5) {
                    switch ("test") {
                        case "okay":
                            continue;
                        default:
                            continue;
                    }
                }
                System.out.println("wow x2!");
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    package com.wdbyte.decompiler;
    
    import java.util.*;
    import org.benf.cfr.reader.util.functors.*;
    import java.util.stream.*;
    
    public class HardCode<A, B>
    {
        private final List<Integer> stuff;
        
        public HardCode(final A a, final B b) {
            (this.stuff = new ArrayList<Integer>()).add(1);
            this.stuff.add(2);
        }
        
        public static void test(final int... args) {
        }
        
        public static void main(final String... args) {
            test(1, 2, 3, 4, 5, 6);
        }
        
        int byteAnd0() {
            int b = 1;
            final int x = 0;
            do {
                b = (byte)(b ^ x);
            } while (b++ < 10);
            return b;
        }
        
        private void a(final Integer i) {
            this.a(i);
            this.b(i);
            this.c(i);
        }
        
        private void b(final int i) {
            this.a(i);
            this.b(i);
            this.c(i);
        }
        
        private void c(final double d) {
            this.c(d);
            this.d(d);
        }
        
        private void d(final Double d) {
            this.c(d);
            this.d(d);
        }
        
        private void e(final Short s) {
            this.b(s);
            this.c(s);
            this.e(s);
            this.f(s);
        }
        
        private void f(final short s) {
            this.b(s);
            this.c(s);
            this.e(s);
            this.f(s);
        }
        
        void test1(final String path) {
            try {}
            catch (NullPointerException t) {
                System.out.println("File Not found");
                if (path == null) {
                    return;
                }
                throw t;
            }
            finally {
                System.out.println("Fred");
                if (path == null) {
                    throw new IllegalStateException();
                }
            }
        }
        
        public static int plus(final boolean t, final int a, final int b) {
            final int c = t ? a : b;
            return c;
        }
        
        Integer lambdaInvoker(final int arg, final UnaryFunction<Integer, Integer> fn) {
            return (Integer)fn.invoke((Object)arg);
        }
        
        public int testLambda() {
            return this.lambdaInvoker(3, (UnaryFunction<Integer, Integer>)(x -> x + 1));
        }
        
        public Integer testLambda(final List<Integer> stuff, final int y, final boolean b) {
            return stuff.stream().filter(b ? (x -> x > y) : (x -> x < 3)).findFirst().orElse(null);
        }
        
        public static <Y extends Integer> void testStream(final List<Y> list) {
            final IntStream s = list.stream().filter(x -> {
                System.out.println(x);
                return x / 2 == 0;
            }).map(x -> x + 2).mapToInt(x -> x);
            s.toArray();
        }
        
        public void testSwitch1() {
            final int i = 0;
            final String string = (Object)(i + 1L) + "";
            switch (string) {
                case "1": {
                    System.out.println("one");
                    break;
                }
            }
        }
        
        public void testSwitch2(final String string) {
            switch (string) {
                case "apples": {
                    System.out.println("apples");
                    break;
                }
                case "pears": {
                    System.out.println("pears");
                    break;
                }
            }
        }
        
        public static void testSwitch3(final int x) {
            while (true) {
                if (x < 5) {
                    final String s = "test";
                    switch (s) {
                        case "okay": {
                            continue;
                        }
                        default: {
                            continue;
                        }
                    }
                }
                else {
                    System.out.println("wow x2!");
                }
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    /*
     * Decompiled with CFR 0.151.
     */
    package com.wdbyte.decompiler;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.stream.IntStream;
    import org.benf.cfr.reader.util.functors.UnaryFunction;
    
    public class HardCode<A, B> {
        private final List<Integer> stuff = new ArrayList<Integer>();
    
        public HardCode(A a, B b) {
            this.stuff.add(1);
            this.stuff.add(2);
        }
    
        public static void test(int ... args) {
        }
    
        public static void main(String ... args) {
            HardCode.test(1, 2, 3, 4, 5, 6);
        }
    
        int byteAnd0() {
            byte by;
            byte b = 1;
            boolean x = false;
            do {
                by = b = (byte)(b ^ (x ? 1 : 0));
                b = (byte)(b + 1);
            } while (by < 10);
            return b;
        }
    
        private void a(Integer i) {
            this.a(i);
            this.b(i);
            this.c(i.intValue());
        }
    
        private void b(int i) {
            this.a(i);
            this.b(i);
            this.c(i);
        }
    
        private void c(double d) {
            this.c(d);
            this.d(d);
        }
    
        private void d(Double d) {
            this.c(d);
            this.d(d);
        }
    
        private void e(Short s) {
            this.b(s.shortValue());
            this.c(s.shortValue());
            this.e(s);
            this.f(s);
        }
    
        private void f(short s) {
            this.b(s);
            this.c(s);
            this.e(s);
            this.f(s);
        }
    
        void test1(String path) {
            try {
                int n = 3;
            }
            catch (NullPointerException t) {
                System.out.println("File Not found");
                if (path == null) {
                    return;
                }
                throw t;
            }
            finally {
                System.out.println("Fred");
                if (path == null) {
                    throw new IllegalStateException();
                }
            }
        }
    
        public static int plus(boolean t, int a, int b) {
            int c = t ? a : b;
            return c;
        }
    
        Integer lambdaInvoker(int arg, UnaryFunction<Integer, Integer> fn) {
            return fn.invoke(arg);
        }
    
        public int testLambda() {
            return this.lambdaInvoker(3, x -> x + 1);
        }
    
        public Integer testLambda(List<Integer> stuff, int y, boolean b) {
            return stuff.stream().filter(b ? x -> x > y : x -> x < 3).findFirst().orElse(null);
        }
    
        public static <Y extends Integer> void testStream(List<Y> list) {
            IntStream s = list.stream().filter(x -> {
                System.out.println(x);
                return x / 2 == 0;
            }).map(x -> x + 2).mapToInt(x -> x);
            s.toArray();
        }
    
        public void testSwitch1() {
            int i = 0;
            switch (Long.valueOf((long)i + 1L) + "") {
                case "1": {
                    System.out.println("one");
                }
            }
        }
    
        public void testSwitch2(String string) {
            switch (string) {
                case "apples": {
                    System.out.println("apples");
                    break;
                }
                case "pears": {
                    System.out.println("pears");
                }
            }
        }
    
        public static void testSwitch3(int x) {
            block6: while (true) {
                if (x < 5) {
                    switch ("test") {
                        case "okay": {
                            continue block6;
                        }
                    }
                    continue;
                }
                System.out.println("wow x2!");
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    import com.wdbyte.decompiler.HardCode;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.stream.IntStream;
    import org.benf.cfr.reader.util.functors.UnaryFunction;
    
    public class HardCode<A, B> {
      private final List<Integer> stuff;
      
      public HardCode(A a, B b) {
        this.stuff = new ArrayList<>();
        this.stuff.add(Integer.valueOf(1));
        this.stuff.add(Integer.valueOf(2));
      }
      
      public static void test(int... args) {}
      
      public static void main(String... args) {
        test(new int[] { 1, 2, 3, 4, 5, 6 });
      }
      
      int byteAnd0() {
        int b = 1;
        int x = 0;
        while (true) {
          b = (byte)(b ^ x);
          if (b++ >= 10)
            return b; 
        } 
      }
      
      private void a(Integer i) {
        a(i);
        b(i.intValue());
        c(i.intValue());
      }
      
      private void b(int i) {
        a(Integer.valueOf(i));
        b(i);
        c(i);
      }
      
      private void c(double d) {
        c(d);
        d(Double.valueOf(d));
      }
      
      private void d(Double d) {
        c(d.doubleValue());
        d(d);
      }
      
      private void e(Short s) {
        b(s.shortValue());
        c(s.shortValue());
        e(s);
        f(s.shortValue());
      }
      
      private void f(short s) {
        b(s);
        c(s);
        e(Short.valueOf(s));
        f(s);
      }
      
      void test1(String path) {
        try {
          byte b = 3;
        } catch (NullPointerException t) {
          System.out.println("File Not found");
          if (path == null)
            return; 
          throw t;
        } finally {
          System.out.println("Fred");
          if (path == null)
            throw new IllegalStateException(); 
        } 
      }
      
      public static int plus(boolean t, int a, int b) {
        int c = t ? a : b;
        return c;
      }
      
      Integer lambdaInvoker(int arg, UnaryFunction<Integer, Integer> fn) {
        return (Integer)fn.invoke(Integer.valueOf(arg));
      }
      
      public int testLambda() {
        return lambdaInvoker(3, x -> Integer.valueOf(x.intValue() + 1)).intValue();
      }
      
      public Integer testLambda(List<Integer> stuff, int y, boolean b) {
        return stuff.stream().filter(b ? (x -> (x.intValue() > y)) : (x -> (x.intValue() < 3))).findFirst().orElse(null);
      }
      
      public static <Y extends Integer> void testStream(List<Y> list) {
        IntStream s = list.stream().filter(x -> {
              System.out.println(x);
              return (x.intValue() / 2 == 0);
            }).map(x -> Integer.valueOf(x.intValue() + 2)).mapToInt(x -> x.intValue());
        s.toArray();
      }
      
      public void testSwitch1() {
        int i = 0;
        switch (Long.valueOf(i + 1L) + "") {
          case "1":
            System.out.println("one");
            break;
        } 
      }
      
      public void testSwitch2(String string) {
        switch (string) {
          case "apples":
            System.out.println("apples");
            break;
          case "pears":
            System.out.println("pears");
            break;
        } 
      }
      
      public static void testSwitch3(int x) {
        while (true) {
          while (x < 5) {
            switch ("test") {
              case "okay":
                break;
            } 
          } 
          System.out.println("wow x2!");
        } 
      }
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    package com.wdbyte.decompiler;
    
    import java.util.ArrayList;
    import java.util.List;
    import org.benf.cfr.reader.util.functors.UnaryFunction;
    
    /*  JADX ERROR: Class process error: JadxRuntimeException
        jadx.core.utils.exceptions.JadxRuntimeException: Code generation error after restart
        	at jadx.core.codegen.CodeGen.wrapCodeGen(CodeGen.java:52)
        	at jadx.core.codegen.CodeGen.generateJavaCode(CodeGen.java:34)
        	at jadx.core.codegen.CodeGen.generate(CodeGen.java:22)
        	at jadx.core.ProcessClass.process(ProcessClass.java:64)
        	at jadx.core.ProcessClass.generateCode(ProcessClass.java:88)
        	at jadx.core.dex.nodes.ClassNode.decompile(ClassNode.java:258)
        	at jadx.core.dex.nodes.ClassNode.decompile(ClassNode.java:221)
        Caused by: jadx.core.utils.exceptions.JadxRuntimeException: Method generation error
        	at jadx.core.codegen.ClassGen.addMethod(ClassGen.java:298)
        	at jadx.core.codegen.ClassGen.lambda$addInnerClsAndMethods$2(ClassGen.java:264)
        	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
        	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
        	at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:395)
        	at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258)
        Caused by: jadx.core.utils.exceptions.CodegenException: Error generate insn: 0x001c: INVOKE  
          (wrap: java.util.stream.IntStream : 0x0018: INVOKE  (r0v0 's' java.util.stream.IntStream) = 
          (wrap: java.util.stream.Stream<R> : 0x0010: INVOKE  (r1v2 java.util.stream.Stream<R>) = 
          (wrap: java.util.stream.Stream<Y extends java.lang.Integer> : 0x0008: INVOKE  (r1v1 java.util.stream.Stream<Y extends java.lang.Integer>) = 
          (wrap: java.util.stream.Stream<Y extends java.lang.Integer> : 0x0000: INVOKE  (r1v0 java.util.stream.Stream<Y extends java.lang.Integer>) = (r3v0 'list' java.util.List<Y extends java.lang.Integer> A[D('list' java.util.List<Y extends java.lang.Integer>)]) type: INTERFACE call: java.util.List.stream():java.util.stream.Stream)
          (wrap: java.util.function.Predicate<? super Y extends java.lang.Integer> : 0x0007: MOVE_RESULT  (r2v0 java.util.function.Predicate<? super Y extends java.lang.Integer>) = )
         type: INTERFACE call: java.util.stream.Stream.filter(java.util.function.Predicate):java.util.stream.Stream)
          (wrap: java.util.function.Function<? super Y extends java.lang.Integer, ? extends R> : 0x000f: MOVE_RESULT  (r2v1 java.util.function.Function<? super Y extends java.lang.Integer, ? extends R>) = )
         type: INTERFACE call: java.util.stream.Stream.map(java.util.function.Function):java.util.stream.Stream)
          (wrap: java.util.function.ToIntFunction<? super R> : 0x0017: MOVE_RESULT  (r2v2 java.util.function.ToIntFunction<? super R>) = )
         type: INTERFACE call: java.util.stream.Stream.mapToInt(java.util.function.ToIntFunction):java.util.stream.IntStream)
         type: INTERFACE call: java.util.stream.IntStream.toArray():int[] in method: com.wdbyte.decompiler.HardCode.testStream(java.util.List<Y extends java.lang.Integer>):void, file: /var/folders/z4/q_08v96n28j2v3rgybvhytvr0000gp/T/jadx-10342693507538465510/classes.dex
        	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:255)
        	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:217)
        	at jadx.core.codegen.RegionGen.makeSimpleBlock(RegionGen.java:110)
        	at jadx.core.codegen.RegionGen.makeRegion(RegionGen.java:56)
        	at jadx.core.codegen.RegionGen.makeSimpleRegion(RegionGen.java:93)
        	at jadx.core.codegen.RegionGen.makeRegion(RegionGen.java:59)
        	at jadx.core.codegen.MethodGen.addRegionInsns(MethodGen.java:244)
        	at jadx.core.codegen.MethodGen.addInstructions(MethodGen.java:237)
        	at jadx.core.codegen.ClassGen.addMethodCode(ClassGen.java:342)
        	at jadx.core.codegen.ClassGen.addMethod(ClassGen.java:295)
        	... 5 more
        Caused by: jadx.core.utils.exceptions.CodegenException: Error generate insn: 0x0018: INVOKE  (r0v0 's' java.util.stream.IntStream) = 
          (wrap: java.util.stream.Stream<R> : 0x0010: INVOKE  (r1v2 java.util.stream.Stream<R>) = 
          (wrap: java.util.stream.Stream<Y extends java.lang.Integer> : 0x0008: INVOKE  (r1v1 java.util.stream.Stream<Y extends java.lang.Integer>) = 
          (wrap: java.util.stream.Stream<Y extends java.lang.Integer> : 0x0000: INVOKE  (r1v0 java.util.stream.Stream<Y extends java.lang.Integer>) = (r3v0 'list' java.util.List<Y extends java.lang.Integer> A[D('list' java.util.List<Y extends java.lang.Integer>)]) type: INTERFACE call: java.util.List.stream():java.util.stream.Stream)
          (wrap: java.util.function.Predicate<? super Y extends java.lang.Integer> : 0x0007: MOVE_RESULT  (r2v0 java.util.function.Predicate<? super Y extends java.lang.Integer>) = )
         type: INTERFACE call: java.util.stream.Stream.filter(java.util.function.Predicate):java.util.stream.Stream)
          (wrap: java.util.function.Function<? super Y extends java.lang.Integer, ? extends R> : 0x000f: MOVE_RESULT  (r2v1 java.util.function.Function<? super Y extends java.lang.Integer, ? extends R>) = )
         type: INTERFACE call: java.util.stream.Stream.map(java.util.function.Function):java.util.stream.Stream)
          (wrap: java.util.function.ToIntFunction<? super R> : 0x0017: MOVE_RESULT  (r2v2 java.util.function.ToIntFunction<? super R>) = )
         type: INTERFACE call: java.util.stream.Stream.mapToInt(java.util.function.ToIntFunction):java.util.stream.IntStream in method: com.wdbyte.decompiler.HardCode.testStream(java.util.List<Y extends java.lang.Integer>):void, file: /var/folders/z4/q_08v96n28j2v3rgybvhytvr0000gp/T/jadx-10342693507538465510/classes.dex
        	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:255)
        	at jadx.core.codegen.InsnGen.addWrappedArg(InsnGen.java:119)
        	at jadx.core.codegen.InsnGen.addArg(InsnGen.java:103)
        	at jadx.core.codegen.InsnGen.addArgDot(InsnGen.java:87)
        	at jadx.core.codegen.InsnGen.makeInvoke(InsnGen.java:715)
        	at jadx.core.codegen.InsnGen.makeInsnBody(InsnGen.java:367)
        	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:249)
        	... 14 more
        Caused by: jadx.core.utils.exceptions.CodegenException: Error generate insn: 0x0010: INVOKE  (r1v2 java.util.stream.Stream<R>) = 
          (wrap: java.util.stream.Stream<Y extends java.lang.Integer> : 0x0008: INVOKE  (r1v1 java.util.stream.Stream<Y extends java.lang.Integer>) = 
          (wrap: java.util.stream.Stream<Y extends java.lang.Integer> : 0x0000: INVOKE  (r1v0 java.util.stream.Stream<Y extends java.lang.Integer>) = (r3v0 'list' java.util.List<Y extends java.lang.Integer> A[D('list' java.util.List<Y extends java.lang.Integer>)]) type: INTERFACE call: java.util.List.stream():java.util.stream.Stream)
          (wrap: java.util.function.Predicate<? super Y extends java.lang.Integer> : 0x0007: MOVE_RESULT  (r2v0 java.util.function.Predicate<? super Y extends java.lang.Integer>) = )
         type: INTERFACE call: java.util.stream.Stream.filter(java.util.function.Predicate):java.util.stream.Stream)
          (wrap: java.util.function.Function<? super Y extends java.lang.Integer, ? extends R> : 0x000f: MOVE_RESULT  (r2v1 java.util.function.Function<? super Y extends java.lang.Integer, ? extends R>) = )
         type: INTERFACE call: java.util.stream.Stream.map(java.util.function.Function):java.util.stream.Stream in method: com.wdbyte.decompiler.HardCode.testStream(java.util.List<Y extends java.lang.Integer>):void, file: /var/folders/z4/q_08v96n28j2v3rgybvhytvr0000gp/T/jadx-10342693507538465510/classes.dex
        	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:255)
        	at jadx.core.codegen.InsnGen.addWrappedArg(InsnGen.java:119)
        	at jadx.core.codegen.InsnGen.addArg(InsnGen.java:103)
        	at jadx.core.codegen.InsnGen.addArgDot(InsnGen.java:87)
        	at jadx.core.codegen.InsnGen.makeInvoke(InsnGen.java:715)
        	at jadx.core.codegen.InsnGen.makeInsnBody(InsnGen.java:367)
        	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:230)
        	... 20 more
        Caused by: jadx.core.utils.exceptions.CodegenException: Error generate insn: 0x0008: INVOKE  (r1v1 java.util.stream.Stream<Y extends java.lang.Integer>) = 
          (wrap: java.util.stream.Stream<Y extends java.lang.Integer> : 0x0000: INVOKE  (r1v0 java.util.stream.Stream<Y extends java.lang.Integer>) = (r3v0 'list' java.util.List<Y extends java.lang.Integer> A[D('list' java.util.List<Y extends java.lang.Integer>)]) type: INTERFACE call: java.util.List.stream():java.util.stream.Stream)
          (wrap: java.util.function.Predicate<? super Y extends java.lang.Integer> : 0x0007: MOVE_RESULT  (r2v0 java.util.function.Predicate<? super Y extends java.lang.Integer>) = )
         type: INTERFACE call: java.util.stream.Stream.filter(java.util.function.Predicate):java.util.stream.Stream in method: com.wdbyte.decompiler.HardCode.testStream(java.util.List<Y extends java.lang.Integer>):void, file: /var/folders/z4/q_08v96n28j2v3rgybvhytvr0000gp/T/jadx-10342693507538465510/classes.dex
        	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:255)
        	at jadx.core.codegen.InsnGen.addWrappedArg(InsnGen.java:119)
        	at jadx.core.codegen.InsnGen.addArg(InsnGen.java:103)
        	at jadx.core.codegen.InsnGen.addArgDot(InsnGen.java:87)
        	at jadx.core.codegen.InsnGen.makeInvoke(InsnGen.java:715)
        	at jadx.core.codegen.InsnGen.makeInsnBody(InsnGen.java:367)
        	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:230)
        	... 26 more
        Caused by: jadx.core.utils.exceptions.CodegenException: Error generate insn: 0x0007: MOVE_RESULT  (r2v0 java.util.function.Predicate<? super Y extends java.lang.Integer>) =  in method: com.wdbyte.decompiler.HardCode.testStream(java.util.List<Y extends java.lang.Integer>):void, file: /var/folders/z4/q_08v96n28j2v3rgybvhytvr0000gp/T/jadx-10342693507538465510/classes.dex
        	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:255)
        	at jadx.core.codegen.InsnGen.addWrappedArg(InsnGen.java:119)
        	at jadx.core.codegen.InsnGen.addArg(InsnGen.java:103)
        	at jadx.core.codegen.InsnGen.generateMethodArguments(InsnGen.java:806)
        	at jadx.core.codegen.InsnGen.makeInvoke(InsnGen.java:746)
        	at jadx.core.codegen.InsnGen.makeInsnBody(InsnGen.java:367)
        	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:230)
        	... 32 more
        Caused by: jadx.core.utils.exceptions.CodegenException: MOVE_RESULT instruction can be used only in fallback mode
        	at jadx.core.codegen.InsnGen.fallbackOnlyInsn(InsnGen.java:604)
        	at jadx.core.codegen.InsnGen.makeInsnBody(InsnGen.java:542)
        	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:230)
        	... 38 more
        */
    public class HardCode<A, B> {
        private final List<Integer> stuff = new ArrayList();
    
        public HardCode(A a, B b) {
            this.stuff.add(1);
            this.stuff.add(2);
        }
    
        public static void test(int... args) {
        }
    
        public static void main(String... args) {
            test(1, 2, 3, 4, 5, 6);
        }
    
        /* access modifiers changed from: package-private */
        public int byteAnd0() {
            int b = 1;
            while (true) {
                int b2 = (byte) (b ^ 0);
                int b3 = b2 + 1;
                if (b2 >= 10) {
                    return b3;
                }
                b = b3;
            }
        }
    
        private void a(Integer i) {
            a(i);
            b(i.intValue());
            c((double) i.intValue());
        }
    
        private void b(int i) {
            a(Integer.valueOf(i));
            b(i);
            c((double) i);
        }
    
        private void c(double d) {
            c(d);
            d(Double.valueOf(d));
        }
    
        private void d(Double d) {
            c(d.doubleValue());
            d(d);
        }
    
        private void e(Short s) {
            b(s.shortValue());
            c((double) s.shortValue());
            e(s);
            f(s.shortValue());
        }
    
        private void f(short s) {
            b(s);
            c((double) s);
            e(Short.valueOf(s));
            f(s);
        }
    
        /* access modifiers changed from: package-private */
        public void test1(String path) {
            System.out.println("Fred");
            if (path == null) {
                throw new IllegalStateException();
            }
        }
    
        public static int plus(boolean t, int a, int b) {
            return t ? a : b;
        }
    
        /* access modifiers changed from: package-private */
        public Integer lambdaInvoker(int arg, UnaryFunction<Integer, Integer> fn) {
            return (Integer) fn.invoke(Integer.valueOf(arg));
        }
    
        public int testLambda() {
            /*
                r2 = this;
                r0 = 3
                r1 = move-result
                java.lang.Integer r0 = r2.lambdaInvoker(r0, r1)
                int r0 = r0.intValue()
                return r0
            */
            throw new UnsupportedOperationException("Method not decompiled: com.wdbyte.decompiler.HardCode.testLambda():int");
        }
    
        private static /* synthetic */ boolean lambda$testLambda$1(int y, Integer x) {
            return x.intValue() > y;
        }
    
        private static /* synthetic */ boolean lambda$testLambda$2(Integer x) {
            return x.intValue() < 3;
        }
    
        public java.lang.Integer testLambda(java.util.List<java.lang.Integer> r3, int r4, boolean r5) {
            /*
                r2 = this;
                java.util.stream.Stream r1 = r3.stream()
                if (r5 == 0) goto L_0x001a
                r0 = move-result
            L_0x000a:
                java.util.stream.Stream r0 = r1.filter(r0)
                java.util.Optional r0 = r0.findFirst()
                r1 = 0
                java.lang.Object r0 = r0.orElse(r1)
                java.lang.Integer r0 = (java.lang.Integer) r0
                return r0
            L_?:
                r0 = move-result
                goto L_0x000a
            */
            throw new UnsupportedOperationException("Method not decompiled: com.wdbyte.decompiler.HardCode.testLambda(java.util.List, int, boolean):java.lang.Integer");
        }
    
        /*  JADX ERROR: MOVE_RESULT instruction can be used only in fallback mode
            jadx.core.utils.exceptions.CodegenException: MOVE_RESULT instruction can be used only in fallback mode
            	at jadx.core.codegen.InsnGen.fallbackOnlyInsn(InsnGen.java:604)
            	at jadx.core.codegen.InsnGen.makeInsnBody(InsnGen.java:542)
            	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:230)
            	at jadx.core.codegen.InsnGen.addWrappedArg(InsnGen.java:119)
            	at jadx.core.codegen.InsnGen.addArg(InsnGen.java:103)
            	at jadx.core.codegen.InsnGen.generateMethodArguments(InsnGen.java:806)
            	at jadx.core.codegen.InsnGen.makeInvoke(InsnGen.java:746)
            	at jadx.core.codegen.InsnGen.makeInsnBody(InsnGen.java:367)
            	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:230)
            	at jadx.core.codegen.InsnGen.addWrappedArg(InsnGen.java:119)
            	at jadx.core.codegen.InsnGen.addArg(InsnGen.java:103)
            	at jadx.core.codegen.InsnGen.addArgDot(InsnGen.java:87)
            	at jadx.core.codegen.InsnGen.makeInvoke(InsnGen.java:715)
            	at jadx.core.codegen.InsnGen.makeInsnBody(InsnGen.java:367)
            	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:230)
            	at jadx.core.codegen.InsnGen.addWrappedArg(InsnGen.java:119)
            	at jadx.core.codegen.InsnGen.addArg(InsnGen.java:103)
            	at jadx.core.codegen.InsnGen.addArgDot(InsnGen.java:87)
            	at jadx.core.codegen.InsnGen.makeInvoke(InsnGen.java:715)
            	at jadx.core.codegen.InsnGen.makeInsnBody(InsnGen.java:367)
            	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:230)
            	at jadx.core.codegen.InsnGen.addWrappedArg(InsnGen.java:119)
            	at jadx.core.codegen.InsnGen.addArg(InsnGen.java:103)
            	at jadx.core.codegen.InsnGen.addArgDot(InsnGen.java:87)
            	at jadx.core.codegen.InsnGen.makeInvoke(InsnGen.java:715)
            	at jadx.core.codegen.InsnGen.makeInsnBody(InsnGen.java:367)
            	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:249)
            	at jadx.core.codegen.InsnGen.makeInsn(InsnGen.java:217)
            	at jadx.core.codegen.RegionGen.makeSimpleBlock(RegionGen.java:110)
            	at jadx.core.codegen.RegionGen.makeRegion(RegionGen.java:56)
            	at jadx.core.codegen.RegionGen.makeSimpleRegion(RegionGen.java:93)
            	at jadx.core.codegen.RegionGen.makeRegion(RegionGen.java:59)
            	at jadx.core.codegen.MethodGen.addRegionInsns(MethodGen.java:244)
            	at jadx.core.codegen.MethodGen.addInstructions(MethodGen.java:237)
            	at jadx.core.codegen.ClassGen.addMethodCode(ClassGen.java:342)
            	at jadx.core.codegen.ClassGen.addMethod(ClassGen.java:295)
            	at jadx.core.codegen.ClassGen.lambda$addInnerClsAndMethods$2(ClassGen.java:264)
            	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
            	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
            	at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:395)
            	at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258)
            */
        public static <Y extends java.lang.Integer> void testStream(java.util.List<Y> r3) {
            /*
                java.util.stream.Stream r1 = r3.stream()
                r2 = move-result
                java.util.stream.Stream r1 = r1.filter(r2)
                r2 = move-result
                java.util.stream.Stream r1 = r1.map(r2)
                r2 = move-result
                java.util.stream.IntStream r0 = r1.mapToInt(r2)
                r0.toArray()
                return
            */
            throw new UnsupportedOperationException("Method not decompiled: com.wdbyte.decompiler.HardCode.testStream(java.util.List):void");
        }
    
        private static /* synthetic */ boolean lambda$testStream$3(Integer x) {
            System.out.println(x);
            return x.intValue() / 2 == 0;
        }
    
        public void testSwitch1() {
            String str = Long.valueOf(((long) 0) + 1) + "";
            char c = 65535;
            switch (str.hashCode()) {
                case 49:
                    if (str.equals("1")) {
                        c = 0;
                        break;
                    }
                    break;
            }
            switch (c) {
                case 0:
                    System.out.println("one");
                    return;
                default:
                    return;
            }
        }
    
        public void testSwitch2(String string) {
            char c = 65535;
            switch (string.hashCode()) {
                case -1411061671:
                    if (string.equals("apples")) {
                        c = 0;
                        break;
                    }
                    break;
                case 106540109:
                    if (string.equals("pears")) {
                        c = 1;
                        break;
                    }
                    break;
            }
            switch (c) {
                case 0:
                    System.out.println("apples");
                    return;
                case 1:
                    System.out.println("pears");
                    return;
                default:
                    return;
            }
        }
    
        public static void testSwitch3(int x) {
            while (true) {
                if (x < 5) {
                    char c = 65535;
                    switch ("test".hashCode()) {
                        case 3412756:
                            if ("test".equals("okay")) {
                                c = 0;
                                break;
                            }
                            break;
                    }
                    switch (c) {
                    }
                } else {
                    System.out.println("wow x2!");
                }
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    package com.wdbyte.decompiler;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.stream.IntStream;
    import org.benf.cfr.reader.util.functors.UnaryFunction;
    
    public class HardCode {
       private final List stuff = new ArrayList();
    
       public HardCode(Object a, Object b) {
          this.stuff.add(1);
          this.stuff.add(2);
       }
    
       public static void test(int... args) {
       }
    
       public static void main(String... args) {
          test(1, 2, 3, 4, 5, 6);
       }
    
       int byteAnd0() {
          int b = 1;
          byte x = 0;
    
          byte var10000;
          do {
             int b = (byte)(b ^ x);
             var10000 = b;
             b = b + 1;
          } while(var10000 < 10);
    
          return b;
       }
    
       private void a(Integer i) {
          this.a(i);
          this.b(i);
          this.c((double)i);
       }
    
       private void b(int i) {
          this.a(i);
          this.b(i);
          this.c((double)i);
       }
    
       private void c(double d) {
          this.c(d);
          this.d(d);
       }
    
       private void d(Double d) {
          this.c(d);
          this.d(d);
       }
    
       private void e(Short s) {
          this.b(s);
          this.c((double)s);
          this.e(s);
          this.f(s);
       }
    
       private void f(short s) {
          this.b(s);
          this.c((double)s);
          this.e(s);
          this.f(s);
       }
    
       void test1(String path) {
          try {
             boolean var2 = true;
             return;
          } catch (NullPointerException var6) {
             System.out.println("File Not found");
             if (path != null) {
                throw var6;
             }
          } finally {
             System.out.println("Fred");
             if (path == null) {
                throw new IllegalStateException();
             }
    
          }
    
       }
    
       public static int plus(boolean t, int a, int b) {
          int c = t ? a : b;
          return c;
       }
    
       Integer lambdaInvoker(int arg, UnaryFunction fn) {
          return (Integer)fn.invoke(arg);
       }
    
       public int testLambda() {
          return this.lambdaInvoker(3, (x) -> {
             return x + 1;
          });
       }
    
       public Integer testLambda(List stuff, int y, boolean b) {
          return (Integer)stuff.stream().filter(b ? (x) -> {
             return x > y;
          } : (x) -> {
             return x < 3;
          }).findFirst().orElse((Object)null);
       }
    
       public static void testStream(List list) {
          IntStream s = list.stream().filter((x) -> {
             System.out.println(x);
             return x / 2 == 0;
          }).map((x) -> {
             return x + 2;
          }).mapToInt((x) -> {
             return x;
          });
          s.toArray();
       }
    
       public void testSwitch1() {
          int i = 0;
          String var2 = (long)i + 1L + "";
          byte var3 = -1;
          switch(var2.hashCode()) {
          case 49:
             if (var2.equals("1")) {
                var3 = 0;
             }
          default:
             switch(var3) {
             case 0:
                System.out.println("one");
             default:
             }
          }
       }
    
       public void testSwitch2(String string) {
          byte var3 = -1;
          switch(string.hashCode()) {
          case -1411061671:
             if (string.equals("apples")) {
                var3 = 0;
             }
             break;
          case 106540109:
             if (string.equals("pears")) {
                var3 = 1;
             }
          }
    
          switch(var3) {
          case 0:
             System.out.println("apples");
             break;
          case 1:
             System.out.println("pears");
          }
    
       }
    
       public static void testSwitch3(int x) {
          while(true) {
             if (x < 5) {
                String var1 = "test";
                byte var2 = -1;
                switch(var1.hashCode()) {
                case 3412756:
                   if (var1.equals("okay")) {
                      var2 = 0;
                  }
                default:
                   switch(var2) {
                   case 0:
                   }
                }
             } else {
                System.out.println("wow x2!");
             }
          }
       }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    // Make sure to add code blocks to your code group

    # Procyon

    看到 Procyon 的反编译结果,还是比较吃惊的,在正常反编译的情况下,反编译后的代码基本上都是原汁原味。唯一一处反编译后和源码语法上有变化的地方,是一个集合的初始化操作略有不同。

      // 源码
       public HardCode(A a, B b) { }
       private final List<Integer> stuff = new ArrayList<>();{
          stuff.add(1);
          stuff.add(2);
       }
      
      1
      2
      3
      4
      5
      6
      // Procyon 反编译
      private final List<Integer> stuff;
          
      public HardCode(final A a, final B b) {
          (this.stuff = new ArrayList<Integer>()).add(1);
          this.stuff.add(2);
      }
      
      1
      2
      3
      4
      5
      6
      7
      // Make sure to add code blocks to your code group

      而其他部分代码, 比如装箱拆箱,Switch 语法,Lambda 表达式,流式操作以及流程控制等,几乎完全一致,阅读没有障碍。

      装箱拆箱操作反编译后完全一致,没有多余的类型转换代码。

        // 源码
        private void a(Integer i) {
            a(i);
            b(i);
            c(i);
        }
        
        private void b(int i) {
            a(i);
            b(i);
            c(i);
        }
        
        private void c(double d) {
            c(d);
            d(d);
        }
        
        private void d(Double d) {
            c(d);
            d(d);
        }
        
        private void e(Short s) {
            b(s);
            c(s);
            e(s);
            f(s);
        }
        
        private void f(short s) {
            b(s);
            c(s);
            e(s);
            f(s);
        }
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        // Procyon 反编译
        private void a(final Integer i) {
            this.a(i);
            this.b(i);
            this.c(i);
        }
        
        private void b(final int i) {
            this.a(i);
            this.b(i);
            this.c(i);
        }
        
        private void c(final double d) {
            this.c(d);
            this.d(d);
        }
        
        private void d(final Double d) {
            this.c(d);
            this.d(d);
        }
        
        private void e(final Short s) {
            this.b(s);
            this.c(s);
            this.e(s);
            this.f(s);
        }
        
        private void f(final short s) {
            this.b(s);
            this.c(s);
            this.e(s);
            this.f(s);
        }
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        // Make sure to add code blocks to your code group

        Switch 部分也是一致,流程控制部分也没有变化。

          // 源码 switch
          public void testSwitch1(){
              int i = 0;
              switch(((Long)(i + 1L)) + "") {
                  case "1":
                      System.out.println("one");
              }
          }
          public void testSwitch2(String string){
              switch (string) {
                  case "apples":
                      System.out.println("apples");
                      break;
                  case "pears":
                      System.out.println("pears");
                      break;
              }
          }
          public static void testSwitch3(int x) {
              while (true) {
                  if (x < 5) {
                      switch ("test") {
                          case "okay":
                              continue;
                          default:
                              continue;
                      }
                  }
                  System.out.println("wow x2!");
              }
          }
          
          
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32
          33
          // Procyon 反编译
          public void testSwitch1() {
              final int i = 0;
              final String string = (Object)(i + 1L) + "";
              switch (string) {
                  case "1": {
                      System.out.println("one");
                      break;
                  }
              }
          }
          public void testSwitch2(final String string) {
              switch (string) {
                  case "apples": {
                      System.out.println("apples");
                      break;
                  }
                  case "pears": {
                      System.out.println("pears");
                      break;
                  }
              }
          }   
          public static void testSwitch3(final int x) {
              while (true) {
                  if (x < 5) {
                      final String s = "test";
                      switch (s) {
                          case "okay": {
                              continue;
                          }
                          default: {
                              continue;
                          }
                      }
                  }
                  else {
                      System.out.println("wow x2!");
                  }
              }
          }
          
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32
          33
          34
          35
          36
          37
          38
          39
          40
          41
          // Make sure to add code blocks to your code group

          Lambda 表达式和流式操作完全一致。

            // 源码
            // Lambda
            public Integer testLambda(List<Integer> stuff, int y, boolean b) {
                return stuff.stream().filter(b ? x -> x > y : x -> x < 3).findFirst().orElse(null);
            }
            
            // stream
            public static <Y extends Integer> void testStream(List<Y> list) {
                IntStream s = list.stream()
                    .filter(x -> {
                        System.out.println(x);
                        return x.intValue() / 2 == 0;
                        })
                    .map(x -> (Integer)x+2)
                    .mapToInt(x -> x);
                s.toArray();
            }
            
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            // Procyon 反编译
            public Integer testLambda(final List<Integer> stuff, final int y, final boolean b) {
                return stuff.stream().filter(b ? (x -> x > y) : (x -> x < 3)).findFirst().orElse(null);
            }
            
            public static <Y extends Integer> void testStream(final List<Y> list) {
                final IntStream s = list.stream().filter(x -> {
                    System.out.println(x);
                    return x / 2 == 0;
                }).map(x -> x + 2).mapToInt(x -> x);
                s.toArray();
            }
            
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            // Make sure to add code blocks to your code group

            流程控制,反编译后发现丢失了无意义的代码部分,阅读来说并无障碍。

              // 源码
              void test1(String path) {
                  try {
                      int x = 3;
                  } catch (NullPointerException t) {
                      System.out.println("File Not found");
                      if (path == null) { return; }
                      throw t;
                  } finally {
                      System.out.println("Fred");
                      if (path == null) { throw new IllegalStateException(); }
                  }
              }
              
              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              // Procyon 反编译
              void test1(final String path) {
                  try {}
                  catch (NullPointerException t) {
                      System.out.println("File Not found");
                      if (path == null) {
                          return;
                      }
                      throw t;
                  }
                  finally {
                      System.out.println("Fred");
                      if (path == null) {
                          throw new IllegalStateException();
                      }
                  }
              }
              
              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              // Make sure to add code blocks to your code group

              鉴于代码篇幅,下面几种的反编译结果的对比只会列出不同之处,相同之处会直接跳过。

              # CFR

              CFR 的反编译结果多出了类型转换部分,个人来看没有 Procyon 那么原汁原味,不过也算是十分优秀,测试案例中唯一不满意的地方是对 while continue 的处理。

              // CFR 反编译结果
              // 装箱拆箱
              private void e(Short s) {
                 this.b(s.shortValue()); // 装箱拆箱多出了类型转换部分。
                 this.c(s.shortValue()); // 装箱拆箱多出了类型转换部分。
                 this.e(s);
                 this.f(s);
              }
              // 流程控制
              void test1(String path) {
                  try {
                      int n = 3;// 流程控制反编译结果十分满意,原汁原味,甚至此处的无意思代码都保留了。
                  }
                  catch (NullPointerException t) {
                      System.out.println("File Not found");
                      if (path == null) {
                          return;
                      }
                      throw t;
                  }
                  finally {
                      System.out.println("Fred");
                      if (path == null) {
                          throw new IllegalStateException();
                      }
                  }
              }
              // Lambda 和 Stream 操作完全一致,不提。
              // switch 处,反编译后功能一致,但是流程控制有所更改。
              public static void testSwitch3(int x) {
                  block6: while (true) { // 源码中只有 while(true),反编译后多了 block6
                      if (x < 5) {
                          switch ("test") {
                              case "okay": {
                                  continue block6; // 多了 block6
                              }
                          }
                          continue;
                      }
                      System.out.println("wow x2!");
                  }
              }
              
              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              20
              21
              22
              23
              24
              25
              26
              27
              28
              29
              30
              31
              32
              33
              34
              35
              36
              37
              38
              39
              40
              41
              42

              # JD-Core

              JD-Core 和 CFR 一样,对于装箱拆箱操作,反编译后不再一致,多了类型转换部分,而且自动优化了数据类型。个人感觉,如果是反编译后自己阅读,通篇的数据类型的转换优化影响还是挺大的。

              // JD-Core 反编译
              private void d(Double d) {
                c(d.doubleValue()); // 新增了数据类型转换
                d(d);
              }
              
              private void e(Short s) {
                b(s.shortValue()); // 新增了数据类型转换
                c(s.shortValue()); // 新增了数据类型转换
                e(s);
                f(s.shortValue()); // 新增了数据类型转换
              }
              
              private void f(short s) {
                b(s);
                c(s);
                e(Short.valueOf(s)); // 新增了数据类型转换
                f(s);
              }
              // Stream 操作中,也自动优化了数据类型转换,阅读起来比较累。
              public static <Y extends Integer> void testStream(List<Y> list) {
                IntStream s = list.stream().filter(x -> {
                      System.out.println(x);
                      return (x.intValue() / 2 == 0);
                    }).map(x -> Integer.valueOf(x.intValue() + 2)).mapToInt(x -> x.intValue());
                s.toArray();
              }
              
              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              20
              21
              22
              23
              24
              25
              26
              27

              # Jadx

              首先 Jadx 在反编译测试代码时,报出了错误,反编译的结果里也有提示不能反编 Lambda 和 Stream 操作,反编译结果中变量名称杂乱无章,流程控制几乎阵亡,如果你想反编译后生物肉眼阅读,Jadx 肯定不是一个好选择。

              // Jadx 反编译
              private void e(Short s) {
                  b(s.shortValue());// 新增了数据类型转换
                  c((double) s.shortValue());// 新增了数据类型转换
                  e(s);
                  f(s.shortValue());// 新增了数据类型转换
              }
              
              private void f(short s) {
                  b(s);
                  c((double) s);// 新增了数据类型转换
                  e(Short.valueOf(s));// 新增了数据类型转换
                  f(s);
              }
              public int testLambda() { // testLambda 反编译失败
                  /*
                      r2 = this;
                      r0 = 3
                      r1 = move-result
                      java.lang.Integer r0 = r2.lambdaInvoker(r0, r1)
                      int r0 = r0.intValue()
                      return r0
                  */
                  throw new UnsupportedOperationException("Method not decompiled: com.wdbyte.decompiler.HardCode.testLambda():int");
              }
              // Stream 反编译失败
              public static <Y extends java.lang.Integer> void testStream(java.util.List<Y> r3) {
                  /*
                      java.util.stream.Stream r1 = r3.stream()
                      r2 = move-result
                      java.util.stream.Stream r1 = r1.filter(r2)
                      r2 = move-result
                      java.util.stream.Stream r1 = r1.map(r2)
                      r2 = move-result
                      java.util.stream.IntStream r0 = r1.mapToInt(r2)
                      r0.toArray()
                      return
                  */
                  throw new UnsupportedOperationException("Method not decompiled: com.wdbyte.decompiler.HardCode.testStream(java.util.List):void");
              }
              public void testSwitch2(String string) { // switch 操作无法正常阅读,和源码出入较大。
                  char c = 65535;
                  switch (string.hashCode()) {
                      case -1411061671:
                          if (string.equals("apples")) {
                              c = 0;
                              break;
                          }
                          break;
                      case 106540109:
                          if (string.equals("pears")) {
                              c = 1;
                              break;
                          }
                          break;
                  }
                  switch (c) {
                      case 0:
                          System.out.println("apples");
                          return;
                      case 1:
                          System.out.println("pears");
                          return;
                      default:
                          return;
                  }
              }
              
              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              20
              21
              22
              23
              24
              25
              26
              27
              28
              29
              30
              31
              32
              33
              34
              35
              36
              37
              38
              39
              40
              41
              42
              43
              44
              45
              46
              47
              48
              49
              50
              51
              52
              53
              54
              55
              56
              57
              58
              59
              60
              61
              62
              63
              64
              65
              66
              67

              # Fernflower

              Fernflower 的反编译结果总体上还是不错的,不过也有不足,它对变量名称的指定,以及 Switch 字符串时的反编译结果不够理想。

              //反编译后变量命名不利于阅读,有很多 var 变量
              int byteAnd0() {
                 int b = 1;
                 byte x = 0;
              
                 byte var10000;
                 do {
                    int b = (byte)(b ^ x);
                    var10000 = b;
                    b = b + 1;
                 } while(var10000 < 10);
              
                 return b;
              }
              // switch 反编译结果使用了hashCode
              public static void testSwitch3(int x) {
                 while(true) {
                    if (x < 5) {
                       String var1 = "test";
                       byte var2 = -1;
                       switch(var1.hashCode()) {
                       case 3412756: 
                          if (var1.equals("okay")) {
                             var2 = 0;
                         }
                       default:
                          switch(var2) {
                          case 0:
                          }
                       }
                    } else {
                       System.out.println("wow x2!");
                    }
                 }
              }
              
              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              20
              21
              22
              23
              24
              25
              26
              27
              28
              29
              30
              31
              32
              33
              34
              35

              # 总结

              五种反编译工具比较下来,结合反编译速度和代码可读性测试,看起来 CFR 工具胜出,Procyon 紧随其后。CFR 在速度上不落下风,在反编译的代码可读性上,是最好的,主要体现在反编译后的变量命名、装箱拆箱、类型转换,流程控制上,以及对 Lambda 表达式、Stream 流式操作和 Switch 的语法支持上,都非常优秀。根据 CFR 官方介绍,已经支持到 Java 14 语法,而且截止写这篇测试文章时,CFR 最新提交代码时间实在 11 小时之前,更新速度很快。

              文章中部分代码已经上传 GitHub :github.com/niumoo/lab-notes/tree/master/java-decompiler (opens new window)

              订阅

              文章持续更新,订阅可以关注「 程序猿阿朗 」公众号或者未读代码博客。

              文章作者: 程序猿阿朗
              文章链接:https://www.wdbyte.com/2021/05/java-decompiler/
              版权声明:本网站当前文章采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 未读代码!
              #反编译#JVM
              上次更新: 2022/12/05, 08:18:32
              抛弃Eclipse,投入IDEA 的独孤求败江湖
              如何使用 Lombok 进行优雅的编码

              ← 抛弃Eclipse,投入IDEA 的独孤求败江湖 如何使用 Lombok 进行优雅的编码→

              最近更新
              01
              如何搭建一个自己的音乐服务器
              12-04
              02
              JUnit 5 单元测试教程
              11-17
              03
              使用 StringUtils.split 的坑
              11-02
              更多文章>

              提示:评论前请刷新页面,否则评论的可能不是当前文章。

              Theme by Vdoing | Copyright © 2018-2022 程序猿阿朗 | MIT License | 皖ICP备20000567号-1
              • 跟随系统
              • 浅色模式
              • 深色模式
              • 阅读模式