为什么 Java 1.0.2 的接口成员没有设置 ACC_ABSTRACT?
Why interface Member from Java 1.0.2 does not have ACC_ABSTRACT set?
我写了一个简单的 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 页,已强调):
An interface is implicitly abstract (§2.13.1); its ACC_ABSTRACT
flag must be set. An interface cannot be final; its implementation could never be completed (§2.13.1) if it were, so it could not have its ACC_FINAL
flag set.
JVM 规范第 9 版 has similar words to say:
If the ACC_INTERFACE
flag is set, the ACC_ABSTRACT
flag must also be set, and the ACC_FINAL
, ACC_SUPER
, ACC_ENUM
, and ACC_MODULE
flags set must not be set.
Oracle/Sun JVM 是否强制执行 "must be" 的要求?如果有,从什么时候开始?如果不是,为什么 JVM 规范会假装它是必需的?
我不清楚 为什么 但我通过创建以下 类 进行了试验,
用 Java 9 的 javac
编译它们,然后在 Foo
中手动编辑 access_flags
到 0x200
;然后通过不同的 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.
中开始执行它
仍然不确定为什么。
这是一个错误 JDK-4059153:javac 没有为接口设置 ACC_ABSTRACT
。
该错误已在 1.2 中修复,但由于已经有许多 classes 编译了此错误,JVM 得到了一个解决方法,可以为所有 classes 自动添加 ACC_ABSTRACT
ACC_INTERFACE
。这一直有效到 Java 6,最终决定严格遵循更新的 class 文件的规范。但是,为了向后兼容旧版本的 class 文件,解决方法直到现在仍然存在,请参阅 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 字节码解析器来做一些实验,最近它在一个意想不到的地方失败了。从 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 页,已强调):
An interface is implicitly abstract (§2.13.1); its
ACC_ABSTRACT
flag must be set. An interface cannot be final; its implementation could never be completed (§2.13.1) if it were, so it could not have itsACC_FINAL
flag set.
JVM 规范第 9 版 has similar words to say:
If the
ACC_INTERFACE
flag is set, theACC_ABSTRACT
flag must also be set, and theACC_FINAL
,ACC_SUPER
,ACC_ENUM
, andACC_MODULE
flags set must not be set.
Oracle/Sun JVM 是否强制执行 "must be" 的要求?如果有,从什么时候开始?如果不是,为什么 JVM 规范会假装它是必需的?
我不清楚 为什么 但我通过创建以下 类 进行了试验,
用 Java 9 的 javac
编译它们,然后在 Foo
中手动编辑 access_flags
到 0x200
;然后通过不同的 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.
中开始执行它仍然不确定为什么。
这是一个错误 JDK-4059153:javac 没有为接口设置 ACC_ABSTRACT
。
该错误已在 1.2 中修复,但由于已经有许多 classes 编译了此错误,JVM 得到了一个解决方法,可以为所有 classes 自动添加 ACC_ABSTRACT
ACC_INTERFACE
。这一直有效到 Java 6,最终决定严格遵循更新的 class 文件的规范。但是,为了向后兼容旧版本的 class 文件,解决方法直到现在仍然存在,请参阅 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;
}