阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

class版本

37次阅读
没有评论

共计 2131 个字符,预计需要花费 6 分钟才能阅读完成。

在 Java 开发中,许多童鞋经常被各种版本的 JDK 搞得晕头转向,本节我们就来详细讲解 Java 程序编译后的 class 文件版本问题。

我们通常说的 Java 8,Java 11,Java 17,是指 JDK 的版本,也就是 JVM 的版本,更确切地说,就是 java.exe 这个程序的版本:

$ java -version
java version "17" 2021-09-14 LTS

而每个版本的 JVM,它能执行的 class 文件版本也不同。例如,Java 11 对应的 class 文件版本是 55,而 Java 17 对应的 class 文件版本是 61。

如果用 Java 11 编译一个 Java 程序,输出的 class 文件版本默认就是 55,这个 class 既可以在 Java 11 上运行,也可以在 Java 17 上运行,因为 Java 17 支持的 class 文件版本是 61,表示“最多支持到版本 61”。

如果用 Java 17 编译一个 Java 程序,输出的 class 文件版本默认就是 61,它可以在 Java 17、Java 18 上运行,但不可能在 Java 11 上运行,因为 Java 11 支持的 class 版本最多到 55。如果使用低于 Java 17 的 JVM 运行,会得到一个UnsupportedClassVersionError,错误信息类似:

java.lang.UnsupportedClassVersionError: Xxx has been compiled by a more recent version of the Java Runtime...

只要看到 UnsupportedClassVersionError 就表示当前要加载的 class 文件版本超过了 JVM 的能力,必须使用更高版本的 JVM 才能运行。

打个比方,用 Word 2013 保存一个 Word 文件,这个文件也可以在 Word 2016 上打开。但反过来,用 Word 2016 保存一个 Word 文件,就无法使用 Word 2013 打开。

但是,且慢,用 Word 2016 也可以保存一个格式为 Word 2013 的文件,这样保存的 Word 文件就可以用低版本的 Word 2013 打开,但前提是保存时必须明确指定文件格式兼容 Word 2013。

类似的,对应到 JVM 的 class 文件,我们也可以用 Java 17 编译一个 Java 程序,指定输出的 class 版本要兼容 Java 11(即 class 版本 55),这样编译生成的 class 文件就可以在 Java >=11 的环境中运行。

指定编译输出有两种方式,一种是在 javac 命令行中用参数 --release 设置:

$ javac --release 11 Main.java

参数 --release 11 表示源码兼容 Java 11,编译的 class 输出版本为 Java 11 兼容,即 class 版本 55。

第二种方式是用参数 --source 指定源码版本,用参数 --target 指定输出 class 版本:

$ javac --source 9 --target 11 Main.java

上述命令如果使用 Java 17 的 JDK 编译,它会把源码视为 Java 9 兼容版本,并输出 class 为 Java 11 兼容版本。注意 --release 参数和 --source --target 参数只能二选一,不能同时设置。

然而,指定版本如果低于当前的 JDK 版本,会有一些潜在的问题。例如,我们用 Java 17 编译 Hello.java,参数设置--source 9--target 11

public class Hello {public static void hello(String name) {System.out.println("hello".indent(4));
    }
}

用低于 Java 11 的 JVM 运行 Hello 会得到一个 LinkageError,因为无法加载Hello.class 文件,而用 Java 11 运行 Hello 会得到一个 NoSuchMethodError,因为String.indent() 方法是从 Java 12 才添加进来的,Java 11 的 String 版本根本没有 indent() 方法。

注意

如果使用 –release 11 则会在编译时检查该方法是否在 Java 11 中存在。

因此,如果运行时的 JVM 版本是 Java 11,则编译时也最好使用 Java 11,而不是用高版本的 JDK 编译输出低版本的 class。

如果使用 javac 编译时不指定任何版本参数,那么相当于使用 --release 当前版本 编译,即源码版本和输出版本均为当前版本。

在开发阶段,多个版本的 JDK 可以同时安装,当前使用的 JDK 版本可由 JAVA_HOME 环境变量切换。

源码版本

在编写源代码的时候,我们通常会预设一个源码的版本。在编译的时候,如果用 --source--release指定源码版本,则使用指定的源码版本检查语法。

例如,使用了 lambda 表达式的源码版本至少要为 8 才能编译,使用了 var 关键字的源码版本至少要为 10 才能编译,使用 switch 表达式的源码版本至少要为 12 才能编译,且 12 和 13 版本需要启用 --enable-preview 参数。

小结

高版本的 JDK 可编译输出低版本兼容的 class 文件,但需注意,低版本的 JDK 可能不存在高版本 JDK 添加的类和方法,导致运行时报错。

运行时使用哪个 JDK 版本,编译时就尽量使用同一版本的 JDK 编译源码。

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-08-05发表,共计2131字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中