我已经写了一个简单的Java字节码解析器来进行一些实验,最近它在一个意外的位置失败了。从Java 1.1.8.16的rt.jar
读取java/lang/reflect/Member.java
时,我的解析器很生气,因为Member
像这样开始(请注意丢失的ACC_ABSTRACT
标志):
Classfile Member.class
Last modified Aug 8, 2002; size 350 bytes
MD5 checksum 9a1aaec8e70e9a2ff9d63331cb0ea34e
Compiled from "Member.java"
public interface java.lang.reflect.Member
minor version: 3
major version: 45
flags: (0x0201) ACC_PUBLIC, ACC_INTERFACE
...
Java 1.2.2.17的版本对此进行了更正,并将标志设置为0x0601
(ACC_ABSTRACT | ACC_INTERFACE | ACC_PUBLIC
)。
我可以找到的最早的JVM规范(据称是1.0.2)说这是这样说的(§4.1,第86页,添加了强调):
一个接口隐式抽象(§2.13.1);必须设置其
ACC_ABSTRACT
频率。接口不能是最终的;它的实施永远无法完成(第2.13.1节),因此无法拥有其ACC_FINAL
频率集。
JVM规范的版本9具有类似的词:
如果设置了
ACC_INTERFACE
标志,则还必须设置ACC_ABSTRACT
flag ,而ACC_FINAL
,ACC_SUPER
,ACC_ENUM
,ACC_MODULE
和CC_14标志必须设置。
Oracle/Sun JVM是否强制执行"必须"的要求?如果是这样,什么时候?如果不是,为什么JVM规范会考虑需要假装?
这是一个错误JDK-4059153:Javac未为接口设置ACC_ABSTRACT
。
该错误是在1.2中固定的,但是由于已经有很多类带有此错误的类,因此JVM可以使用ACC_INTERFACE
的所有类都可以自动添加ACC_ABSTRACT
。直到Java 6终于决定严格遵循较新的类文件的规范时,这起作用了。但是,对于与类文件的较旧版本的向后兼容性,到目前为止,解决方法仍然存在,请参阅ClassFileParser.cpp:
if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) {
// Set abstract bit for old class files for backward compatibility
flags |= JVM_ACC_ABSTRACT;
}
我不清楚为什么用Java 9的javac
编译它们,然后在Foo
中手动编辑access_flags
;然后通过不同的Java版本手动骑自行车minor_version
和major_version
,以查看会发生什么:
interface Foo { }
class Bar implements Foo {
public static void main(String[] args) {
System.out.println("BAZ");
}
}
结果:
╭──────╥───────┬───────┬─────╮
│ Java ║ minor │ major │ out │
╞══════╬═══════╪═══════╪═════╡
│ 1.1 ║ 03 │ 2d │ BAZ │
│ 1.2 ║ " │ " │ BAZ │
│ 1.3 ║ " │ 2e │ BAZ │
│ 1.4 ║ 00 │ 2f │ BAZ │
│ 5 ║ 00 │ 31 │ BAZ │
│ 6 ║ 00 │ 32 │ err │
│ 7 ║ 00 │ 33 │ err │
│ 8 ║ 00 │ 34 │ err │
│ 9 ║ 00 │ 35 │ err │
└──────╨───────┴───────┴─────┘
其中" err"确实像这样打印出来:
Error: LinkageError occurred while loading main class Bar
java.lang.ClassFormatError: Illegal class modifiers in class Foo: 0x200
所以我想他们终于在Java 6中实施了它。
仍然不确定为什么。