相同的Java源代码编译成不同的二进制类

相同的Java源代码编译成不同的二进制类,java,compilation,Java,Compilation,有人能解释一下相同的Java源代码最终如何编译成不同的二进制类文件吗 问题产生于以下情况: 我们有一个相当大的应用程序(800多个类),它已经被分支、重组,然后重新集成到主干中。在重新整合之前,我们将主干合并到分支中,这是标准程序 最终结果是一组带有分支源的目录和一组带有主干源的目录。使用超越比较法,我们能够确定两组来源是相同的。然而,在编译时(使用IntellijV11中托管的maven的同一个JDK),我们注意到大约有十几个类文件是不同的 当我们为每一对明显不同的类文件反编译源代码时,我们最

有人能解释一下相同的Java源代码最终如何编译成不同的二进制类文件吗

问题产生于以下情况:

我们有一个相当大的应用程序(800多个类),它已经被分支、重组,然后重新集成到主干中。在重新整合之前,我们将主干合并到分支中,这是标准程序

最终结果是一组带有分支源的目录和一组带有主干源的目录。使用超越比较法,我们能够确定两组来源是相同的。然而,在编译时(使用IntellijV11中托管的maven的同一个JDK),我们注意到大约有十几个类文件是不同的

当我们为每一对明显不同的类文件反编译源代码时,我们最终得到了相同的java源代码,因此就最终结果而言,这似乎并不重要。但为什么只有少数文件是不同的呢

谢谢


其他想法:


如果maven/javac以不同的顺序编译文件,这会影响最终结果吗

不同的JDK产生不同的二进制类(优化,但也有类版本号)。也有编译选项(JDK可能以较旧的格式编译,或者可以添加调试信息)。

不同版本的Java可以添加不同的元数据,而这些元数据通常被反编译器忽略


我建议您尝试使用
javap-c-v
来获取文件中的更多细节。如果这没有帮助,您可以使用asmiferclassvisitor查看每个字节。

当您使用beyond compare进行比较时,会根据文件的内容进行比较。但是在构建过程中,只检查源文件的时间戳是否有更改。因此,如果源文件的上次修改日期发生更改,它将被重新编译。

同一个JDK也可以有不同的输出,具体取决于编译方式。
您可以使用或不使用调试信息进行编译,也可以编译为在旧版本中运行,每个选项都会生成其他类。

假设JDK版本、构建工具版本和构建/编译选项相同,我仍然可以想到一些可能的差异来源:

  • 时间戳-类文件可能1包含编译时间戳。除非在完全相同的时间运行编译,否则同一文件的不同编译将产生不同的时间戳

  • 源文件名路径-每个类文件都包含源文件的路径名。如果编译两个具有不同路径名的树,则类文件将包含不同的源路径名

  • 导入的编译时常量的值-当一个类
    a
    使用另一个类
    B
    中定义的编译时常量(有关“编译时常量”的定义,请参阅JLS),该常量的值将合并到
    a
    s类文件中。因此,如果您针对不同版本的
    B
    (常数值不同)编译
    A
    ,那么
    A
    的代码可能会不同

  • 由于编译器在
    HashMap
    键中使用
    identityHashcode
    而导致的差异可能会导致某些步骤中map迭代顺序的差异。这可能会影响
    .class
    文件的生成,但这种影响并不显著,但仍然表现为
    .class
    文件差异。例如,常量池条目可能以不同的顺序结束

  • 外部类/方法的签名差异;e、 g.如果您更改了一个POM文件中的依赖项版本

  • 有效构建类路径的差异可能导致导入类的查找顺序不同。这反过来可能会导致类文件的常量池中条目的顺序出现不显著的差异。这可能是由于以下原因造成的:

    • 在外部JAR文件目录中以不同顺序出现的文件
    • 由于生成工具迭代源文件时源文件的顺序不同,因此文件的编译顺序不同2,或者
    • 构建中的并行性(如果已启用)
  • 对于文件排序问题,有一种可能的解决方法:使用未记录的
    -XDsortfiles
    选项,如中所述。(感谢@Holger知道这一点。)

    请注意,您通常看不到文件系统目录中文件的实际顺序。命令行工具,如
    ls
    dir
    ,以及文件浏览器通常会在显示条目之前对其进行排序(按名称或时间戳顺序)


    1-这取决于编译器。另外,也不能保证
    javap
    会显示时间戳。。。如果他们在场

    2-操作系统不保证列出目录(在系统调用级别)将以确定的顺序返回文件系统对象。。。或者,如果已删除并重新添加文件,则按相同的顺序



    我要补充一点,找出差异原因的第一步是找出它们到底是什么。您可能需要(需要)通过手动解码一对类文件来识别它们实际存在差异的位置来完成这项工作。。。不同的jdk版本究竟意味着什么?我想不同版本的优化可能会有所不同。使用javap-c-v(感谢Peter Lawrey)并使用Beyond Compare(很棒的工具,我喜欢!)查看各自的输出,我可以确认Stephen c(感谢Stephen c)回答中的第5项给出了部分答案。在一些情况下,恒定池顺序是不同的。但是,我相当肯定这两个类的类路径是相同的,但是编译的顺序可能不同