Java类文件的创建是确定性的吗?

Java类文件的创建是确定性的吗?,java,compiler-construction,javac,Java,Compiler Construction,Javac,当使用相同的JDK(即相同的javac可执行文件)时,生成的类文件是否总是相同的?是否会因操作系统或硬件的不同而有所不同?除了JDK版本之外,还有其他因素导致差异吗?有没有避免差异的编译器选项?这仅仅是理论上的差异,还是Oracle的javac实际上为相同的输入和编译器选项生成了不同的类文件 更新1我感兴趣的是生成,即编译器输出,而不是类文件是否可以在各种平台上运行 update2用“相同的JDK”,我也指相同的javac可执行文件 更新3Oracle编译器中理论差异和实际差异的区别 [编辑,添

当使用相同的JDK(即相同的
javac
可执行文件)时,生成的类文件是否总是相同的?是否会因操作系统或硬件的不同而有所不同?除了JDK版本之外,还有其他因素导致差异吗?有没有避免差异的编译器选项?这仅仅是理论上的差异,还是Oracle的
javac
实际上为相同的输入和编译器选项生成了不同的类文件

更新1我感兴趣的是生成,即编译器输出,而不是类文件是否可以在各种平台上运行

update2用“相同的JDK”,我也指相同的
javac
可执行文件

更新3Oracle编译器中理论差异和实际差异的区别

[编辑,添加释义问题]

“在什么情况下,相同的javac可执行文件在不同的平台上运行时会产生不同的字节码?”

编译器没有义务在每个平台上产生相同的字节码。您应该咨询不同供应商的
javac
实用程序以获得具体答案


我将展示一个关于文件排序的实际示例

假设我们有两个jar文件:
my1.jar
My2.jar
。它们并排放在
lib
目录中。编译器按照字母顺序读取它们(因为这是
lib
),但如果文件系统不区分大小写,则顺序是
my1.jar
My2.jar
,如果文件系统区分大小写,则顺序是
my1.jar

my1.jar
有一个带有方法的类
a.class

public class A {
     public static void a(String s) {}
}
My2.jar
具有相同的
A.class
,但具有不同的方法签名(接受
对象
):

很明显如果你有电话

String s = "x"; 
A.a(s); 

它将在不同的情况下编译具有不同签名的方法调用。因此,根据文件系统的大小写敏感度,您将得到不同的类。

答案很可能是“是”,但要得到准确的答案,您确实需要在编译过程中搜索一些键或guid生成

我不记得发生这种情况的情况了。例如,为了进行序列化,ID是硬编码的,即由程序员或IDE生成

另外,JNI也很重要


p.p.S.我发现
javac
本身就是用java编写的。这意味着它在不同的平台上是相同的。因此,它不会无缘无故地生成不同的代码。因此,它只能在本机呼叫中执行此操作。

简短回答-


长话短说 它们
字节码
对于不同的平台不必相同。是JRE(Java运行时环境)知道如何准确地执行字节码

如果您阅读了,您将了解到这不一定是真的,因为字节码对于不同的平台是相同的

在这里,它将类文件的结构显示为

ClassFile {
    u4 magic;
    u2 minor_version;
    u2 major_version;
    u2 constant_pool_count;
    cp_info constant_pool[constant_pool_count-1];
    u2 access_flags;
    u2 this_class;
    u2 super_class;
    u2 interfaces_count;
    u2 interfaces[interfaces_count];
    u2 fields_count;
    field_info fields[fields_count];
    u2 methods_count;
    method_info methods[methods_count];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}
检查次要版本和主要版本

次要版本、主要版本

次要版本的值和 主要版本项是此版本的次要版本号和主要版本号 类文件。主版本号和次版本号共同决定 类文件格式的版本。如果类文件具有主版本 数字M和次要版本号M,表示其版本 类文件格式为M.M。因此,类文件格式版本可能为 按字典顺序排列,例如,1.5<2.0<2.1。爪哇 虚拟机实现可以支持 版本v当且仅当v位于某个连续范围Mi.0 v Mj.m。只有Sun可以指定Java虚拟机的版本范围 符合特定版本的机器实施 Java平台可能支持.1

阅读更多的脚注

1 Sun JDK 1.0.2版的Java虚拟机实现 支持45.0至45.3版本(含)的类文件格式。太阳的 JDK发行版1.1.X可以支持中版本的类文件格式 范围为45.0至45.65535(含)。版本1.2的实现 Java 2平台的一个组件可以支持中版本的类文件格式 范围为45.0到46.0(含45.0)


因此,调查所有这些表明,在不同平台上生成的类文件不必完全相同。

我相信,如果使用相同的JDK,生成的字节码将始终相同,与所使用的硬件和操作系统无关。字节码生成由java编译器完成,它使用确定性算法将源代码“转换”为字节码。因此,输出总是相同的。在这些情况下,只有对源代码的更新才会影响输出。

Java允许您在一个平台上编写/编译代码,并在不同的平台上运行。
AFAIK;只有当在不同平台上生成的类文件相同或技术上相同(即相同)时,这才可能实现

编辑

我所说的技术上相同的评论的意思是。如果逐字节比较,它们不需要完全相同

因此根据规范。不同平台上的类的类文件不需要逐字节匹配。

我们这样说:

我可以很容易地生成一个完全一致的Java编译器,在给定相同的
.Java
文件的情况下,它不会两次生成相同的
.class
文件

我可以通过调整各种字节码结构或简单地向方法中添加多余的属性(这是允许的)来做到这一点

鉴于该规范不要求编译器生成逐字节相同的类文件,我将避免
ClassFile {
    u4 magic;
    u2 minor_version;
    u2 major_version;
    u2 constant_pool_count;
    cp_info constant_pool[constant_pool_count-1];
    u2 access_flags;
    u2 this_class;
    u2 super_class;
    u2 interfaces_count;
    u2 interfaces[interfaces_count];
    u2 fields_count;
    field_info fields[fields_count];
    u2 methods_count;
    method_info methods[methods_count];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}
Can there be a difference depending on the operating system or hardware? 
What are the circumstances where the same javac executable, when run on a different platform, will produce different bytecode?