当前位置: 首页 > article >正文

CFR( Java 反编译器)---> lambda 表达式底层实现机制

一、安装教程

CFR(Class File Reader)是一个流行的Java反编译器,它可以将编译后的.class文件或整个.jar文件转换回Java源代码。以下是CFR的下载和使用教程:

下载CFR

  1. 访问CFR的官方网站或GitHub仓库:CFR的最新版本和所有历史版本都可以在其GitHub仓库中找到。访问 CFR的GitHub页面。

  2. 选择版本并下载:在GitHub仓库中,点击“Releases”部分,找到你想要下载的版本。通常,你应该下载最新的稳定版本。点击对应版本旁边的.jar文件链接进行下载。

使用CFR反编译.class文件

一旦你下载了CFR的.jar文件,你就可以使用Java命令行工具来运行它并反编译.class文件或.jar文件。确保你的计算机上已经安装了Java。

  1. 打开命令行工具:在Windows上,你可以使用CMD或PowerShell;在macOS或Linux上,使用Terminal。

  2. 使用CFR反编译.class文件:在命令行中,导航到包含CFR .jar文件的目录。使用以下命令反编译单个.class文件:

    java -jar cfr-0.152.jar path/to/YourClass.class
    

    cfr-0.152.jar替换为你下载的CFR版本的实际文件名,将path/to/YourClass.class替换为你想要反编译的.class文件的路径。

  3. 使用CFR反编译.jar文件:CFR也可以反编译整个.jar文件中的所有类。使用以下命令:

    java -jar cfr-0.152.jar path/to/your-archive.jar
    

    path/to/your-archive.jar替换为你想要反编译的.jar文件的路径。

高级用法

CFR提供了许多命令行选项,允许你定制反编译过程。例如,你可以控制是否显示局部变量名、是否解包枚举等。要查看所有可用的选项,运行:

java -jar cfr-0.152.jar --help

这将列出所有支持的选项及其描述。

注意事项

  • 反编译的代码可能与原始源代码在某些细节上有所不同,特别是变量名和某些代码结构可能会因编译过程中的优化而改变。
  • 使用反编译器查看代码主要用于学习和分析目的。如果你打算使用反编译的代码作为你自己的项目的一部分,请确保你有权这样做,以避免侵犯版权。

CFR是一个非常强大的工具,适用于需要深入理解编译后的Java代码的开发者。

二、使用帮助介绍

java -jar cfr-0.152.jar --help

2.1 help选项介绍

% java -jar cfr-0.152.jar --help
CFR 0.152
   --aexagg                         (boolean)
   --aexagg2                        (boolean)
   --aggressivedocopy               (int >= 0)  default: 0
   --aggressivedoextension          (boolean)
   --aggressiveduff                 (boolean)
   --aggressivesizethreshold        (int >= 0)  default: 13000
   --allowcorrecting                (boolean)  default: true
   --allowmalformedswitch           (boolean)
   --analyseas                      (One of [DETECT, JAR, WAR, CLASS])
   --antiobf                        (boolean)  default: false
   --arrayiter                      (boolean)  default: true if class file from version 49.0 (Java 5) or greater
   --caseinsensitivefs              (boolean)  default: true
   --clobber                        (boolean)
   --collectioniter                 (boolean)  default: true if class file from version 49.0 (Java 5) or greater
   --commentmonitors                (boolean)  default: false
   --comments                       (boolean)  default: true
   --constobf                       (boolean)  default: Value of option 'antiobf'
   --decodeenumswitch               (boolean)  default: true if class file from version 49.0 (Java 5) or greater
   --decodefinally                  (boolean)  default: true
   --decodelambdas                  (boolean)  default: true if class file from version 52.0 (Java 8) or greater
   --decodestringswitch             (boolean)  default: true if class file from version 51.0 (Java 7) or greater
   --dumpclasspath                  (boolean)  default: false
   --eclipse                        (boolean)  default: true
   --elidescala                     (boolean)  default: false
   --extraclasspath                 (string)
   --forbidanonymousclasses         (boolean)  default: false
   --forbidmethodscopedclasses      (boolean)  default: false
   --forceclassfilever              (string, specifying either java version as 'j6', 'j1.0', or classfile as '56', '56.65535')
   --forcecondpropagate             (boolean)
   --forceexceptionprune            (boolean)
   --forcereturningifs              (boolean)
   --forcetopsort                   (boolean)
   --forcetopsortaggress            (boolean)
   --forcetopsortnopull             (boolean)
   --forloopaggcapture              (boolean)
   --hidebridgemethods              (boolean)  default: Value of option 'obfattr'
   --hidelangimports                (boolean)  default: true
   --hidelongstrings                (boolean)  default: false
   --hideutf                        (boolean)  default: true
   --ignoreexceptions               (boolean)  default: false
   --ignoreexceptionsalways         (boolean)  default: false
   --importfilter                   (string)
   --innerclasses                   (boolean)  default: true
   --instanceofpattern              (boolean)  default: true if class file from version 60.0 (Java 16) or greater, or experimental in 58.0 (Java 14), 59.0 (Java 15)
   --j14classobj                    (boolean)  default: false if class file from version 49.0 (Java 5) or greater
   --jarfilter                      (string)
   --labelledblocks                 (boolean)  default: true
   --lenient                        (boolean)  default: false
   --liftconstructorinit            (boolean)  default: true
   --lomem                          (boolean)  default: false
   --methodname                     (string)
   --obfattr                        (boolean)  default: Value of option 'antiobf'
   --obfcontrol                     (boolean)  default: Value of option 'antiobf'
   --obfuscationpath                (string)
   --outputdir                      (string)
   --outputencoding                 (string)
   --outputpath                     (string)
   --override                       (boolean)  default: true if class file from version 50.0 (Java 6) or greater
   --previewfeatures                (boolean)  default: true
   --pullcodecase                   (boolean)  default: false
   --recordtypes                    (boolean)  default: true if class file from version 60.0 (Java 16) or greater, or experimental in 58.0 (Java 14), 59.0 (Java 15)
   --recover                        (boolean)  default: true
   --recovertypeclash               (boolean)
   --recovertypehints               (boolean)
   --reducecondscope                (boolean)
   --relinkconst                    (boolean)  default: true
   --relinkconststring              (boolean)  default: Value of option 'relinkconst'
   --removebadgenerics              (boolean)  default: true
   --removeboilerplate              (boolean)  default: true
   --removedeadconditionals         (boolean)
   --removedeadmethods              (boolean)  default: true
   --removeinnerclasssynthetics     (boolean)  default: true
   --rename                         (boolean)  default: false
   --renamedupmembers               (boolean)  default: Value of option 'rename'
   --renameenumidents               (boolean)  default: Value of option 'rename'
   --renameillegalidents            (boolean)  default: Value of option 'rename'
   --renamesmallmembers             (int >= 0)  default: 0
   --sealed                         (boolean)  default: true if class file from version 62.0 (Java 18) or greater, or experimental in 60.0 (Java 16), 61.0 (Java 17)
   --showinferrable                 (boolean)  default: false if class file from version 51.0 (Java 7) or greater
   --showversion                    (boolean)  default: true
   --silent                         (boolean)  default: false
   --skipbatchinnerclasses          (boolean)  default: true
   --staticinitreturn               (boolean)  default: true
   --stringbuffer                   (boolean)  default: false if class file from version 49.0 (Java 5) or greater
   --stringbuilder                  (boolean)  default: true if class file from version 49.0 (Java 5) or greater
   --stringconcat                   (boolean)  default: true if class file from version 53.0 (Java 9) or greater
   --sugarasserts                   (boolean)  default: true
   --sugarboxing                    (boolean)  default: true
   --sugarenums                     (boolean)  default: true if class file from version 49.0 (Java 5) or greater
   --sugarretrolambda               (boolean)  default: false
   --switchexpression               (boolean)  default: true if class file from version 58.0 (Java 14) or greater, or experimental in 56.0 (Java 12), 57.0 (Java 13)
   --tidymonitors                   (boolean)  default: true
   --trackbytecodeloc               (boolean)  default: false
   --tryresources                   (boolean)  default: true if class file from version 51.0 (Java 7) or greater
   --usenametable                   (boolean)  default: true
   --usesignatures                  (boolean)  default: true
   --version                        (boolean)  default: true
   --help                           (string)
Please specify '--help optionname' for specifics, e.g.

作用:

  • --aexagg--aexagg2:控制异常聚合的程度,可能有助于简化生成的代码。
  • --aggressivedocopy:更积极地复制代码片段,可能使反编译的代码更易于理解。
  • --aggressivedoextension--aggressiveduff:控制CFR在处理某些代码结构时的积极程度。
  • --aggressivesizethreshold:设置积极处理代码的大小阈值。
  • --allowcorrecting:允许CFR进行一些修正以提高代码准确性。
  • --allowmalformedswitch:允许处理格式错误的switch语句。
  • --analyseas:指定分析的类型(DETECT, JAR, WAR, CLASS)。
  • --antiobf:针对混淆代码的处理。
  • --arrayiter--collectioniter:控制数组和集合迭代的处理。
  • --caseinsensitivefs:文件系统大小写不敏感。
  • --clobber:覆盖现有文件。
  • --commentmonitors:在生成的代码中注释掉monitor语句。
  • --comments:生成的代码中包含注释。
  • --constobf:处理常量混淆。
  • --decodeenumswitch--decodefinally--decodelambdas--decodestringswitch:控制特定结构的反编译。
  • --dumpclasspath:输出类路径。
  • --eclipse:为Eclipse生成更友好的代码。
  • --elidescala:省略Scala特定的代码。
  • --extraclasspath:指定额外的类路径。
  • --forbidanonymousclasses--forbidmethodscopedclasses:禁止处理匿名类和方法作用域类。
  • --forceclassfilever:强制指定类文件版本。
  • --forcecondpropagate--forceexceptionprune--forcereturningifs--forcetopsort--forcetopsortaggress--forcetopsortnopull:控制特定代码结构的处理方式。
  • --forloopaggcapture:循环聚合捕获。
  • --hidebridgemethods--hidelangimports--hidelongstrings--hideutf:隐藏或省略代码中的特定部分。
  • --ignoreexceptions--ignoreexceptionsalways:忽略异常处理。
  • --importfilter:过滤导入。
  • --innerclasses:处理内部类。
  • --instanceofpattern:处理instanceof模式匹配(Java 16+特性)。
  • --j14classobj:处理Java 14的类对象。
  • --jarfilter:过滤JAR文件中的类。
  • --labelledblocks:标记代码块。
  • --lenient:宽容模式。
  • --liftconstructorinit:提升构造函数初始化。
  • --lomem:低内存模式。
  • --methodname:指定方法名。
  • --obfattr--obfcontrol:控制混淆属性。
  • --obfuscationpath:指定混淆路径。
  • --outputdir--outputencoding--outputpath:输出目录、编码和路径。
  • --override:处理覆盖方法。
  • --previewfeatures:启用预览特性。
  • --pullcodecase:代码提取。
  • --recordtypes:处理记录类型(Java 16+特性)。
  • --recover--recovertypeclash--recovertypehints:恢复类型和提示。
  • --reducecondscope:减少条件作用域。
  • --relinkconst--relinkconststring:重新链接常量。
  • --removebadgenerics--removeboilerplate--removedeadconditionals--removedeadmethods--removeinnerclasssynthetics:移除无用代码。
  • --rename--renamedupmembers--renameenumidents--renameillegalidents--renamesmallmembers:重命名标识符。
  • --sealed:处理密封类(Java 17+特性)。
  • --showinferrable:显示可推断的信息。
  • --showversion:显示版本信息。
  • --silent:静默模式。
  • --skipbatchinnerclasses:跳过批处理内部类。
  • --staticinitreturn:静态初始化返回。
  • --stringbuffer--stringbuilder--stringconcat:字符串处理。
  • --sugarasserts--sugarboxing--sugarenums--sugarretrolambda:语法糖处理。
  • --switchexpression:处理switch表达式(Java 12+特性)。
  • --tidymonitors:整理监视器。
  • --trackbytecodeloc:跟踪字节码位置。
  • --tryresources:处理try-with-resources。
  • --usenametable--usesignatures:使用名称表和签名。
  • --version:显示CFR版本。
  • --help:显示帮助信息。

这些选项提供了对CFR反编译过程的细粒度控制,允许用户根据需要调整反编译的行为和输出。

2.2 常用选项

在使用CFR(Class File Reader)反编译器时,一些选项比其他选项更为常用,因为它们提供了对反编译过程中最基本和最直接的控制。以下是一些最常用的CFR选项:

  1. --outputdir <directory>:指定输出目录,反编译的代码将被保存到这个目录下。这是在处理多个类文件或整个jar文件时非常有用的选项。

  2. --outputpath <file>:指定输出文件的路径,反编译的代码将被保存到这个文件中。当你只关心一个类或少数几个类的反编译结果时,这个选项很有用。

  3. --decodelambdas:尝试反编译Lambda表达式。由于Lambda表达式是Java 8及更高版本中的一个重要特性,这个选项通常被启用以获得更准确的反编译结果。

  4. --showversion:显示CFR的版本信息。这对于确保你正在使用的是最新版本的CFR或者对版本有特定要求时很有帮助。

  5. --silent:减少CFR在反编译过程中的输出信息。当你不需要详细的进度信息时,这个选项可以让输出更加简洁。

  6. --comments:在生成的代码中包含注释。这可以帮助理解反编译的代码,尤其是当原始代码中包含了有用的注释时。

  7. --renameillegalidents:重命名不合法的标识符。这对于处理混淆过的代码特别有用,因为混淆器可能会生成不符合Java标识符命名规则的名称。

  8. --stringbuilder:使用StringBuilder而不是StringBuffer。这反映了较新Java版本的实践,因为StringBuilder通常比StringBuffer更优。

  9. --tryresources:尝试反编译try-with-resources语句。这是Java 7引入的一个重要特性,能够自动管理资源。

  10. --help:显示帮助信息,包括所有可用的命令行选项及其描述。这是了解和探索CFR功能的起点。

这些选项中的许多都是为了提高反编译的准确性、可读性或者操作的便利性。根据你的具体需求,你可能会发现其中一些选项特别有用。

三、举例

3.1 查看 lambda 底层实现机制

1.lambda 表达式的本质:函数式接口的匿名子类的匿名对象
2.lambda表达式是语法糖

语法糖:编码时是lambda简洁的表达式,在字节码期,语法糖会被转换为实际复杂的实现方式,含义不变;即编码表面有个糖衣,在编译期会被脱掉

在 Java 中,当你使用 lambda 表达式时,编译器在背后做了很多工作来将这些表达式转换成可执行的代码。特别是,它使用了 invokedynamic 指令和 LambdaMetafactory 来动态生成和加载 lambda 表达式对应的类。这是 Java 8 引入的一种新机制,用于提高 lambda 表达式的性能和灵活性。

3.1.1 反编译 lambda

当使用--decodelambdas false选项运行CFR反编译器时,CFR将不会尝试以Lambda表达式的形式反编译Java 8及以上版本中的Lambda表达式。相反,它会以匿名类或类似的结构来展示这些Lambda表达式。这可能会使得反编译的代码看起来与原始源代码有较大差异,特别是在使用Lambda表达式的地方。

原始Java代码

假设我们有以下简单的Java程序,它使用Lambda表达式来遍历并打印一个字符串列表:

import java.util.Arrays;
import java.util.List;

public class LambdaExample {
    public static void main(String[] args) {
        List<String> items = Arrays.asList("Apple", "Banana", "Cherry");
        items.forEach(item -> System.out.println(item));
    }
}

当这段代码被编译时,对于 items.forEach(item -> System.out.println(item)); 这一行,编译器生成的字节码会使用 invokedynamic 指令来引导 lambda 表达式的实现。这个过程涉及到 LambdaMetafactory,它是 JDK 提供的一个工厂类,专门用于生成符合特定函数式接口的类实例。

LambdaMetafactory.metafactory 是一个静态方法,它接受一系列参数,包括目标接口的方法类型、lambda 表达式的实现方法的方法句柄(Method Handle),以及 lambda 表达式的签名。它返回一个 CallSite 对象,这个对象封装了对动态生成的 lambda 表达式类的实例的调用。

在运行时,invokedynamic 指令首次执行时会调用这个 metafactory 方法,生成一个实现了 Consumer<String> 接口的类的实例。这个实例具体执行的代码就是 lambda 表达式的体。

编译Java代码

首先,我们需要编译这个Java程序。在命令行中,使用javac命令编译这个程序:

javac LambdaExample.java

这将生成LambdaExample.class文件。

使用CFR反编译并应用--decodelambdas false

现在,我们将使用CFR反编译器来反编译这个.class文件,并使用--decodelambdas false选项来禁止Lambda表达式的特殊处理。在命令行中,运行以下命令:

java -jar cfr-0.152.jar LambdaExample.class --decodelambdas false

请确保将cfr-0.152.jar替换为你的CFR .jar文件的实际路径。

反编译结果

由于--decodelambdas false选项的使用,CFR将不会以Lambda表达式的形式展示原始的Lambda表达式。相反,它可能会生成一个匿名类或类似的结构来表示Lambda表达式的功能。反编译的输出可能看起来像这样:

/*
 * Decompiled with CFR 0.152.
 */
import java.lang.invoke.LambdaMetafactory;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class LambdaExample {
    public static void main(String[] stringArray) {
        List<String> list = Arrays.asList("Apple", "Banana", "Cherry");
        list.forEach(
        (Consumer<String>)LambdaMetafactory.metafactory(
        null, 
        null, 
        null, 
        (Ljava/lang/Object;)V, 
        lambda$main$0(java.lang.String ), 
        (Ljava/lang/String;)V)());
    }

    private static /* synthetic */ void lambda$main$0(String string) {
        System.out.println(string);
    }
}

3.1.2 反编译结果解释

这段代码是一个使用CFR反编译器反编译后的Java程序,其中包含了Lambda表达式的使用。原始的Java代码可能使用了Lambda表达式来遍历一个字符串列表并打印每个元素。下面是对这段反编译代码的解释:

导入部分
import java.lang.invoke.LambdaMetafactory;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

这部分代码导入了程序中使用到的类。LambdaMetafactory是Java在运行时动态生成和调用Lambda表达式的关键工具。ArraysListConsumer则是在集合操作中常用的类和接口。

类定义
public class LambdaExample {
    ...
}

定义了一个名为LambdaExample的公共类。

主方法
public static void main(String[] stringArray) {
    List<String> list = Arrays.asList("Apple", "Banana", "Cherry");
    list.forEach(
    (Consumer<String>)LambdaMetafactory.metafactory(null,
     	null, 
    	null,
      	(Ljava/lang/Object;)V, 
      	lambda$main$0(java.lang.String ), 
      	(Ljava/lang/String;)V)());
}

这是程序的主方法,它创建了一个包含三个字符串的列表,并使用forEach方法遍历这个列表。在原始的Java代码中,这里很可能使用了一个Lambda表达式来打印列表中的每个元素。在反编译的代码中,Lambda表达式被转换成了对LambdaMetafactory.metafactory方法的调用,这个方法在运行时动态生成了一个实现了Consumer接口的类的实例。因为forEach方法入参就是一个函数式接口Consumer<? super T>,即:最终返回Consumer实例对象

default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    } 
Lambda表达式的静态方法
private static /* synthetic */ void lambda$main$0(String string) {
    System.out.println(string);
}

这个静态方法是原始Lambda表达式的体现。在编译时,Java编译器将Lambda表达式转换为类中的一个私有静态方法。这里,lambda$main$0方法接收一个字符串参数,并打印它。

总结

这段反编译的代码展示了Java中Lambda表达式的底层实现机制。原始的Lambda表达式被转换为一个私有静态方法,而LambdaMetafactory用于在运行时动态生成一个符合Lambda表达式行为的Consumer实例。这种机制使得Java能够在保持类型安全的同时,提供对Lambda表达式的高效支持。


http://www.kler.cn/a/325342.html

相关文章:

  • 储能技术中锂离子电池的优势和劣势
  • 大数据新视界 -- 大数据大厂之 Impala 性能飞跃:分区修剪优化的应用案例(下)(22 / 30)
  • js导入导出
  • 群控系统服务端开发模式-应用开发-前端图片格式功能开发
  • Godot的开发框架应当是什么样子的?
  • Python酷库之旅-第三方库Pandas(218)
  • 【计算机网络】网络层详解
  • 解决Pymysql has no attribute ‘escape_string‘ 并且无法引入该模块
  • 爱速搭百度低代码开发平台
  • 视频汇聚EasyCVR视频监控平台调取接口提示“认证过期”是什么原因?
  • 通信工程师笔记
  • 生成对抗网络(Generative Adversarial Networks, GANs)
  • Pymsql 插入数据时报错 Unkown column xxxx in field list
  • 昇思MindSpore进阶教程--轻量化数据处理
  • 【HTTPS】中间人攻击和证书的验证
  • 手写SpringMVC
  • Subdominator:一款针对漏洞奖励计划的子域名安全枚举工具
  • Rustrover2024.2 正式发布:个人非商用免费,泰裤辣
  • 更新日志-Python OS
  • 动态规划(3)——dp多状态问题Ⅰ
  • 云舟观测:集成开源Grafana Faro构建前端页面性能监控平台
  • Linux开机logo设置
  • Spring MVC 拦截器总结
  • 数据结构:树、森林
  • 峟思助力堤防工程安全:构建多功能防洪屏障
  • Qt+toml文件读写