资讯专栏INFORMATION COLUMN

javap命令与Java Dcompiler工具、IDEA自带的反编译器反编译的结果的差别及原因

张宪坤 / 973人阅读

摘要:反汇编器与反编译器不同,反编译器的目标是高级语言而非汇编语言。反汇编器的反汇编输出通常格式化为适合人类阅读,而非用作汇编器的输入源,因此它主要是一个逆向工程工具。本文章参考了通过命令分析汇编指令反汇编器

问题描述

写这篇文章是为了记录我这几天遇到的一个疑惑,并且顺藤摸瓜的学习一下javap命令。遇到的疑惑是这样的:我在看“使用枚举类型实现单列模式”的博客时,发现一些博客中写到的枚举类型的反编译结果包含的信息不尽相同:
  一些对枚举类的反编译结果仅仅包含像我们正常编写的枚举类的一些信息,如使用IDEA,Java Decompiler;
  而另一些反编译结果则完全不同:一方面枚举类成为了普通的class类,只是它继承了Enum类,枚举值都是public static final形式的对象;另一方面还多了static块,values(),valueof()这些方法。

源代码:

public enum Season {
    SPIRNG,
    SUMMER,
    AUTUMN,
    WINTER;

    public String allSeasons() {
        return SPIRNG.name() + " " + SUMMER.name() + " " + AUTUMN.name() +" " +WINTER.name();
    }
}

使用javap -c Season.class命令反编译结果:

public final class Season extends java.lang.Enum {
  public static final Season SPIRNG;

  public static final Season SUMMER;

  public static final Season AUTUMN;

  public static final Season WINTER;

  public static Season[] values();
    Code:
       0: getstatic     #1                  // Field $VALUES:[LSeason;
       3: invokevirtual #2                  // Method "[LSeason;".clone:()Ljava/lang/Object;
       6: checkcast     #3                  // class "[LSeason;"
       9: areturn

  public static Season valueOf(java.lang.String);
    Code:
       0: ldc           #4                  // class Season
       2: aload_0
       3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
       6: checkcast     #4                  // class Season
       9: areturn

  public java.lang.String allSeasons();
    Code:
       0: new           #7                  // class java/lang/StringBuilder
       3: dup
       4: invokespecial #8                  // Method java/lang/StringBuilder."":()V
       7: getstatic     #9                  // Field SPIRNG:LSeason;
      10: invokevirtual #10                 // Method name:()Ljava/lang/String;
      13: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      16: ldc           #12                 // String
      18: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      21: getstatic     #13                 // Field SUMMER:LSeason;
      24: invokevirtual #10                 // Method name:()Ljava/lang/String;
      27: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      30: ldc           #12                 // String
      32: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      35: getstatic     #14                 // Field AUTUMN:LSeason;
      38: invokevirtual #10                 // Method name:()Ljava/lang/String;
      41: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      44: ldc           #12                 // String
      46: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      49: getstatic     #15                 // Field WINTER:LSeason;
      52: invokevirtual #10                 // Method name:()Ljava/lang/String;
      55: invokevirtual #11                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      58: invokevirtual #16                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      61: areturn

  static {};
    Code:
       0: new           #4                  // class Season
       3: dup
       4: ldc           #17                 // String SPIRNG
       6: iconst_0
       7: invokespecial #18                 // Method "":(Ljava/lang/String;I)V
      10: putstatic     #9                  // Field SPIRNG:LSeason;
      13: new           #4                  // class Season
      16: dup
      17: ldc           #19                 // String SUMMER
      19: iconst_1
      20: invokespecial #18                 // Method "":(Ljava/lang/String;I)V
      23: putstatic     #13                 // Field SUMMER:LSeason;
      26: new           #4                  // class Season
      29: dup
      30: ldc           #20                 // String AUTUMN
      32: iconst_2
      33: invokespecial #18                 // Method "":(Ljava/lang/String;I)V
      36: putstatic     #14                 // Field AUTUMN:LSeason;
      39: new           #4                  // class Season
      42: dup
      43: ldc           #21                 // String WINTER
      45: iconst_3
      46: invokespecial #18                 // Method "":(Ljava/lang/String;I)V
      49: putstatic     #15                 // Field WINTER:LSeason;
      52: iconst_4
      53: anewarray     #4                  // class Season
      56: dup
      57: iconst_0
      58: getstatic     #9                  // Field SPIRNG:LSeason;
      61: aastore
      62: dup
      63: iconst_1
      64: getstatic     #13                 // Field SUMMER:LSeason;
      67: aastore
      68: dup
      69: iconst_2
      70: getstatic     #14                 // Field AUTUMN:LSeason;
      73: aastore
      74: dup
      75: iconst_3
      76: getstatic     #15                 // Field WINTER:LSeason;
      79: aastore
      80: putstatic     #1                  // Field $VALUES:[LSeason;
      83: return
}

使用Java Decompiler工具的反编译结果:

public enum Season
{
  SPIRNG,  SUMMER,  AUTUMN,  WINTER;
  
  private Season() {}
  
  public String allSeasons()
  {
    return SPIRNG.name() + " " + SUMMER.name() + " " + AUTUMN.name() + " " + WINTER.name();
  }
}

从上面反编译的结果我们总结差别如下

javap反编译的结果中类的声明不同,它是一个继承了Enum类并且声明为final类型的类;

javap反编译结果中包含的方法不同,它包含了static(),valueOf(),values()方法;

javap反编译结果中的方法体不同,它的方法体使用jvm指令来描述。

javap命令
C:Usersw00457192>javap -h
用法: javap  
其中, 可能的选项包括:
  -? -h --help -help               输出此帮助消息
  -version                         版本信息
  -v  -verbose                     输出附加信息
  -l                               输出行号和本地变量表
  -public                          仅显示公共类和成员
  -protected                       显示受保护的/公共类和成员
  -package                         显示程序包/受保护的/公共类和成员 (默认)
  -p  -private                     显示所有类和成员
  -c                               对代码进行反汇编
  -s                               输出内部类型签名
  -sysinfo                         显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)
  -constants                       显示最终常量
  --module <模块>, -m <模块>       指定包含要反汇编的类的模块
  --module-path <路径>             指定查找应用程序模块的位置
  --system                    指定查找系统模块的位置
  --class-path <路径>              指定查找用户类文件的位置
  -classpath <路径>                指定查找用户类文件的位置
  -cp <路径>                       指定查找用户类文件的位置
  -bootclasspath <路径>            覆盖引导类文件的位置

从上面的信息我们可以看出javap -c是用于反汇编,而不是反编译。参考维基百科的解释如下:

反汇编器(disassembler)是一种将机器语言转换为汇编语言的计算机程序——这与汇编器的目的相反。反汇编器与反编译器不同,反编译器的目标是高级语言而非汇编语言。反汇编器的反汇编输出通常格式化为适合人类阅读,而非用作汇编器的输入源,因此它主要是一个逆向工程工具。
结论

现在我们可以理解为什么javap和Java Decompiler的输出中方法体有所不同了。

至于为什么Java Decompiler没有包含static块和values(),valueof()方法,我推测是因为反编译器的目的是反编译出源码,而我们可以确定的是static块和values()和valueof()这两个方法是编译器自己实现的,不是我们人为添加的,所以在反编译器看来这不属于源码的一部分,因此没有在编译的结果中包含这些方法。

本文章参考了
  通过javap命令分析java汇编指令
  反汇编器

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77697.html

相关文章

  • JAVA语法糖和语法糖编译

    摘要:提供给了用户大量的语法糖,比如泛型自动装箱拆箱循环变长参数内部类枚举类断言新特性方法引用等解语法糖语法糖的存在主要是方便开发人员使用。 首先,部分总结文字引用 简书作者:Eric新之助 。链接:https://www.jianshu.com/p/4de08deb6ba4 已获得授权 showImg(https://segmentfault.com/img/bVbfuX9?w=646&...

    weakish 评论0 收藏0
  • Javag工程师成神之路(2019正式版)

    摘要:结构型模式适配器模式桥接模式装饰模式组合模式外观模式享元模式代理模式。行为型模式模版方法模式命令模式迭代器模式观察者模式中介者模式备忘录模式解释器模式模式状态模式策略模式职责链模式责任链模式访问者模式。 主要版本 更新时间 备注 v1.0 2015-08-01 首次发布 v1.1 2018-03-12 增加新技术知识、完善知识体系 v2.0 2019-02-19 结构...

    Olivia 评论0 收藏0
  • Java 运行时获取方法参数名

    摘要:原文如果觉得我的文章对你有用,请随意赞赏本文整理运行时获取方法参数名的两种方法,的最新的方法和之前的方法。文件中的调试信息上文介绍了通过新增的反射运行时获取方法参数名。 原文:http://nullwy.me/2017/04/java...如果觉得我的文章对你有用,请随意赞赏 本文整理 Java 运行时获取方法参数名的两种方法,Java 8 的最新的方法和 Java 8 之前的方法。 ...

    cfanr 评论0 收藏0
  • 你和阿里资深架构师之间,差不仅仅是年龄(进阶必看)

    摘要:导读阅读本文需要有足够的时间,笔者会由浅到深带你一步一步了解一个资深架构师所要掌握的各类知识点,你也可以按照文章中所列的知识体系对比自身,对自己进行查漏补缺,觉得本文对你有帮助的话,可以点赞关注一下。目录一基础篇二进阶篇三高级篇四架构篇五扩 导读:阅读本文需要有足够的时间,笔者会由浅到深带你一步一步了解一个资深架构师所要掌握的各类知识点,你也可以按照文章中所列的知识体系对比自身,对自己...

    huaixiaoz 评论0 收藏0
  • 一个简单例子教会您使用javap

    摘要:从字节码的分析可以观察到一个有趣的现象,再次看看我们的语句。这张表里每行的后面的数字代表源代码的序号,冒号后面的数字代表字节码里每行指令的序号。维护了源代码同字节指令的映射关系,确保了代码调试的顺利进行。 javap是JDK自带的工具: showImg(https://segmentfault.com/img/remote/1460000016730237); 这篇文章使用下面这段简单...

    BLUE 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<