.java中的注释更改.class文件的ByteCode

时间:2018-03-27 18:53:44

标签: java bytecode

我的理解是Java编译器在输出.class文件中的字节码中不包含注释。

但是我有代码:

InputStream stream = getClassLoader().getResourceAsStream(classAsPath);
byte[] classBytes = IOUtils.toByteArray(stream);

我为我的java类运行它,在java代码中添加注释,然后重新运行它。我看到classBytes与添加的评论

不同

2 个答案:

答案 0 :(得分:2)

注释不包含在类文件中。但是,编译器默认包含一些用于调试目的的元数据,其中包括行号。如果您的注释跨越一行或更多,这显然会更改所有内容的行号,因此生成的类文件将更改。

您可以将-g:none传递给javac,以避免包含行号和其他可选元数据。

答案 1 :(得分:1)

没有保证相同的源代码产生完全相同的字节代码。该标准没有强制要求某些语言结构如何映射到字节代码结构,并且某些“最佳实践”随着时间的推移而发生了变化,或者在编译器实现之间有所不同。一些方面,例如类文件中方法的顺序被认为是完全不相关的,可能是编译器的某些实现特定方面的结果,而不是故意的决定。

通常,使用完全相同的编译器实现和版本将重现相同的字节代码,因为编译器的程序代码是确定性的,但不必如此,例如,一些哈希映射实现具有随机化功能,编译器也可能利用并行处理,这可能会增加与结果无关的方面的不确定性。

鉴于即使完全相同的源代码也不能保证产生相同的字节码,即使很小的,实际上不相关的更改也可能导致字节码的差异,因为即使没有其他任何改变,它们也可能对时序产生影响。或哈希码。

正如其他人所说,插入注释行可能会更改默认包含的调试属性中的行号,因此这是获取不同字节代码的简单原因。但重要的是要理解你总是得到相同字节代码的假设从根本上是有缺陷的。

请注意,类似的错误假设已经导致Java API中的主要设计缺陷。默认serialVersionUID是通过散列类数据计算的,这些数据实际上与序列化数据的兼容性无关。正如the documentation of Serializable所述:

  

如果可序列化类没有显式声明serialVersionUID,则序列化运行时将根据类的各个方面计算该类的默认serialVersionUID值,如Java(TM)对象序列化规范中所述。但是,强烈建议所有可序列化类都显式声明serialVersionUID值,因为默认的serialVersionUID计算对类细节高度敏感,这些细节可能因编译器实现而异,因此可能导致意外{{1在反序列化期间。

不要重复这个错误。