我编写了一个简单的Java字节码解析器来进行一些实验,最近它在一个意想不到的地方失败了。从Java 1.1.8.16的['training', 'working', 'carrying']
中读取java/lang/reflect/Member.java
时,我的解析器生气,因为rt.jar
就像这样开始(注意缺少Member
标志):
ACC_ABSTRACT
Java 1.2.2.17中的版本对此进行了更正,并将标记设置为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
...
(0x0601
)。
我能找到的最早的JVM规范(据称是1.0.2)就是这样说的(§4.1,p.86,强调增加):
接口是隐式抽象的(§2.13.1);必须设置
ACC_ABSTRACT | ACC_INTERFACE | ACC_PUBLIC
fl ag 。界面不能是最终的;如果它的实现永远不会完成(§2.13.1),那么它就不能设置ACC_ABSTRACT
fl。
JVM规范的第9版has similar words to say:
如果设置了
ACC_FINAL
标记,则还必须设置ACC_INTERFACE
标记 ,ACC_ABSTRACT
,ACC_FINAL
,{{1不得设置{}和ACC_SUPER
标志。
Oracle / Sun JVM是否强制执行“必须”这样的要求?如果是这样,那么什么时候如果没有,为什么JVM规范会假装它是必需的?
答案 0 :(得分:4)
这是一个错误JDK-4059153:javac没有为接口设置ACC_ABSTRACT
。
该错误已在1.2中修复,但由于已经编译了许多使用此错误的类,因此JVM得到了一种解决方法,可以为ACC_ABSTRACT
的所有类自动添加ACC_INTERFACE
。直到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;
}
答案 1 :(得分:0)
我不清楚为什么但是我通过创建以下类来进行实验,
使用Java 9 -silent
编译它们,然后在javac
中手动编辑access_flags
到0x200
;然后通过不同的Java版本手动循环Foo
和minor_version
以查看会发生什么:
major_version
结果:
interface Foo { }
class Bar implements Foo {
public static void main(String[] args) {
System.out.println("BAZ");
}
}
哪里&#34;错误&#34;真打印出这样的:
╭──────╥───────┬───────┬─────╮
│ 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 │
└──────╨───────┴───────┴─────┘
所以我猜他们终于开始在Java 6中强制执行它了。
仍然不确定原因。