12

谁能解释相同的 Java 源代码如何最终编译为二进制不同的类文件?

问题源于以下情况:

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

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

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

谢谢。


补充思想:

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

4

5 回答 5

9

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

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

  2. 源文件名路径 - 每个类文件都包含源文件的路径名。如果您用不同的路径名编译两棵树,则类文件将包含不同的源路径名。

  3. 导入的编译时常量的值 - 当一个类A使用在另一个类B中定义的编译时常量(参见 JLS 中“编译时常量”的定义)时,该常量的值将合并到As 类文件中。因此,如果您A针对不同版本的B(常量值不同)进行编译,则代码A可能会有所不同。

  4. 由于编译器identityHashcodeHashMap键中使用的差异可能导致某些步骤中映射迭代顺序的差异。这可能会.class以不显着的方式影响文件生成,但仍显示为.class文件差异。例如,常量池条目可能以不同的顺序结束。

  5. 外部类/方法的签名差异;例如,如果您更改了其中一个 POM 文件中的依赖项版本。

  6. 有效构建类路径的差异可能会导致找到导入类的顺序不同。这可能反过来导致类文件的常量池中的条目顺序出​​现非显着差异。这可能由于以下原因而发生:

    • 在外部 JAR 文件的目录中以不同顺序出现的文件,
    • 由于构建工具迭代时源文件的顺序不同,文件以不同的顺序编译2,或者
    • 构建中的并行性(如果您启用了该功能)。

文件排序问题有一个可能的解决方法:使用JDK-7003006-XDsortfiles中描述的未记录选项。(感谢@Holger 知道这一点。)

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


1 - 这取决于编译器。此外,不能保证javap会显示时间戳......如果它们存在的话。

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


I should add that the first step to identifying the cause of the differences is to work out exactly what they are. You probably need (needed) to do that the hard way - by manually decoding a pair of class files to identify the places where they actually differences ... and what the differences actually mean.

于 2012-10-01T12:24:47.667 回答
2

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

于 2012-10-01T11:21:09.513 回答
1

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

于 2012-10-01T11:18:32.477 回答
1

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

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

于 2012-10-01T11:20:20.017 回答
1

根据您的编译方式,相同的 JDK 也可以有不同的输出。您可以使用或不使用调试信息进行编译,您可以编译以在旧版本中运行,每个选项都会导致其他类。

于 2012-10-01T11:22:02.430 回答