1
pursuer OP 更正一下,我再次检查文档的时候发现 Java8 的时候 flip 方法是 final 的,但 Java11 后去掉了,导致编译器在高版本下使用了 invokevirtual 指令调用 flip 函数,导致低版本产生异常。上面提到的“JVM 将不同返回值认定为不同方法“这一说法是不成立的。
|
2
pursuer OP 再次更正,我查到 final 关键字对字节码生成没有影响,所以上面说的指令差异也是不对的,那可能还是因为方法签名的问题吧。
|
3
SoloCompany 2020-10-22 22:10:19 +08:00
java 兼容性如果都能挑剔的话别的就更不要说了
你说的的这种问题是常见情形, 如果你一定需要使用高版本的 javac 的话, 正确的做法是给 javac 指定 bootstrapclasspath, 使用 JAVA 8 的 rt.jar, 仅仅靠 -source / -target 参数是无法保证 bytecode 能在低版本的 runtime 下运行 |
4
abcbuzhiming 2020-10-22 22:20:56 +08:00
java 好像从来没保证说高版本编译的 class 可以在低版本 jvm 上跑啊,我记得 jvm 一直说的是向上兼容,即低版本 jdk 编译的 class 可以跑在高版本 jvm 上(但是从 java9 开始这也不完全保证了,好像只保证 3 个版本之类是兼容的);向下兼容没听说过
|
5
pursuer OP |
6
Goooogle 2020-10-22 23:37:05 +08:00
Java 在编译时,会将使用到的方法的签名固化在字节码中的常量池中(类型为 CONSTANT_Methodref_info ),当运行时和编译时的签名不一样时,就会报这个错误。即使是“将参数类型改为其父类型”这种直观看起来可行的方式也不行。
你例子中,ByteBuffer 是 Buffer 的子类型,单纯从语法上讲,把一个方法的 ByteBuffer 参数的类型替换成 Buffer,所有这个方法的调用方都能继续调用,不会有任何问题,但在编译后的方法执行时先去常量池找到对应的符号引用,但该符号引用在运行时环境中没有,不会判断继承关系,而是直接抛出异常。 前段时间刚碰到这个问题。 |