Tips
做一个终身学习的人。
在本章,主要介绍以下内容:
新的JDK版本控制方案是什么
如何使用
Runtime.Version
类解析JDK版本字符串JDK JRE 9的新目录布局是什么
JDK 9中的批注的标准覆盖机制如何工作的
在JDK 9中使用扩展机制的变化
JDK 9中的类加载器如何工作以及模块的加载方式
资源如何封装在JDK 9中的模块中
如何使用
Module
,Class
和ClassLoader
类中的资源查找方法访问模块中的资源jrt URL方案是什么,以及如何使用它来访问运行时映像中的资源
如何访问JDK 9中的JDK内部API以及JDK 9中已删除的JDK API列表
JDK 9中如何使用
--patch-module
命令行选项替换模块中的类和资源
一. 新的JDK版本控制方案
在JDK 9之前,JDK版本控制方案对开发人员来说并不直观,程序解析并不容易。 看看这两个JDK版本,你不能说出他们之间的微妙差异。 很难回答一个简单的问题:哪个版本包含最新的安全修复程序,JDK 7 Update 55或JDK 7 Update 60? 答案不是很明显的,你可能已经猜到了JDK 7 Update 60。这两个版本都包含相同的安全修复程序。 JDK 8 Update 66,1.8.0_66和JDK 8u66版本有什么区别? 它们代表相同的版本。 在了解版本字符串中包含的详细信息之前,有必要详细了解版本控制方案。 JDK 9试图规范JDK版本控制方案,因此人们很容易理解,易于程序解析,并遵循行业标准版本控制方案。
JDK 9包含一个名为Runtime.Version
的静态嵌套类,它表示Java SE平台实现的版本字符串。 它可以用于表示,解析,验证和比较版本字符串。
版本字符串按顺序由以下四个元素组成。 只有第一个是强制性的:
版本号
预发行信息
构建信息
附加信息
以下正则表达式定义版本字符串的格式:
$vnum(-$pre)?(\+($build)?(-$opt)?)?
一个简短版本的字符串由一个版本号码组成,可选地包含预发布信息:
$vnum(-$pre)?
可以使用只包含主版本号“9”的版本字符串。“9.0.1-ea + 154-20170130.07.36am”,包含版本字符串的所有部分。
1. 版本号
版本号是按句点分隔的元素序列。 它可以是任意长度。 其格式如下:
^[1-9][0-9]*(((\.0)*\.[1-9][0-9]*)*)*$
版本号可以由一到四个元素组成,如下所示:
$major.$minor.$security(.$addtionalInfo)
$major
元素代表JDK版本的主要版本。 主要版本是递增的,其中包含重要的新功能。 例如,JDK 8的主要版本为8,对于JDK 9为9。当主版本号增加时,版本号中的所有其他部分都将被删除。 例如,如果版本号为9.2.2.1,则主版本号从9增加到10时,新版本号将为10。
$minor
元素代表JDK版本的次要版本。 增加一个小的更新版本,例如错误修复,新的垃圾收集器,新的JDK特定的API等。
$security
元素表示JDK版本的安全级别更新。 它会增加一个安全更新。 当次要版本号增加时,该元素不会重置。 给定$major
的$security
更高值总是表示更安全的版本。 例如,JDK版本9.1.7与JDK版本9.5.7一样安全,因为两个版本的安全级别是相同的,也就是7。另一个例子,JDK版本9.2.2比9.2.1更安全,因为对于相同的主要版本9,前者的安全级别为2大于后者的安全级别1。
以下规则适用于版本号:
所有元素必须是非负整数。
前三个要素分别被视为主要版本,次要版本和安全级别;其余的(如果存在)被视为附加信息,例如指示补丁发布的数字。
只有主要版本元素是强制性的。
版本号的元素不能包含前导零。 例如,JDK 9的主要版本是9,而不是09。
后面的元素不能为零。 也就是说,版本号不能为9.0.0。 它可以是9,9.2或9.0.x,其中x是正整数。
2. 预发行信息
版本字符串中的$pre
元素是预发行标识符,例如早期访问版本的ea,预发行版快照,以及开发人员内部构建版本。 这是可选的。 如果它存在,它以前缀为连字符( - ),并且必须是与正则表达式([a-zA-Z0-9] +
)匹配的字母数字字符串)。 以下版本字符串包含9作为版本号,ea作为预发布信息。
9-ea
3. 构建信息
版本字符串中的$build
元素是为每个提升的构建增加的构建号。 这是可选的。当版本号的任何部分增加时,它将重置为1。 如果它存在,它加上加号(+),并且必须匹配正则表达式(0 | [1-9] [0-9] *
)。 以下版本的字符串包含154作为版本号。
9-EA+154
4. 附加信息
版本字符串中的$opt
元素包含其他构建信息,例如内部构建的日期和时间。这是可选的。它是字母和数字,可以包含连字符和句点。 如果它存在,它以前缀为连字符(-),并且必须与正则表达式([-a-zA-Z0-9 \。] +)匹配。 如果$build
不存在,则需要在$opt
值前加一个加号,后跟连字符(+ -)来指定$opt
的值。 例如,在9-ea+132-2016-08-23中,$build
为132,$opt
为2016-08-23; 在9+-123中,$pre
和$build
缺失,$opt为123
。以下版本字符串在其附加信息元素中加入发布的日期和时间:
9-EA+154-20170130.07.36am
5. 解析旧版和新版字符串
JDK版本或者是受限更新版本,其中包括新功能和非安全修补程序,或重要补丁更新,其中仅包括针对安全漏洞的修补程序。 版本字符串包括版本号,包括更新号和构建号。 限制更新版本的编号为20的倍数。重要补丁更新使用奇数,通过将五加倍加到先前的限制更新中,并在需要时添加一个以保持计算结果为奇数。 一个例子是1.8.0_31-b13,它是JDK主版本8的更新31。 它的内部版本号是13。注意,在JDK 9之前,版本字符串始终以1开头。
Tips
解析版本字符串以获取JDK版本的主版本的现有代码可能会在JDK 9中失败,具体取决于其使用的逻辑。 例如,如果逻辑通过跳过第一个元素(以前为1)来查找第二个元素的主版本,逻辑将失败。 例如,如果它从1.8.0返回8,那么它将从9.0.1返回0,在那里你会期望9。
6. 系统属性的版本更改
在JDK 9中,包含JDK版本字符串的系统属性返回的值已更改。 下面表格是这些系统属性及其格式的列表。 $vstr
,$vnum
和$pre
分别指版本字符串,版本号和预发布信息。
系统属性名称 | 值 |
---|---|
java.version | $vnum(-$pre)? |
java.runtime.version | $vstr |
java.vm.version | $vstr |
java.specification.version | $vnum |
java.vm.specification.version | $vnum |
7. 使用Runtime.Version
类
DK 9添加了一个名为Runtime.Version
的静态嵌套类,其实例代表版本字符串。 Version
类没有公共构造函数。 获取其实例的唯一方法是调用静态方法parse(String vstr)
。 如果版本字符串为空或无效,该方法可能会抛出运行时异常。
import java.lang.Runtime.Version; ...// Parse a version string "9.0.1-ea+132"Version version = Version.parse("9.0.1-ea+132");
Runtime.Version
类中的以下方法返回版本字符串的元素。 方法名称足够直观,可以猜测它们返回的元素值的类型。
int major()int minor()int security()Optional<String> pre()Optional<Integer> build()Optional<String> optional()
注意,对于可选元素,$pre
,$build
和$opt
,返回类型为Optional
。 对于可选的$minor
和$security
元素,返回类型为int,而不是Optional
,如果版本字符串中缺少$minor
和$security
,则返回零。
回想一下,版本字符串中的版本号可能包含第三个元素之后的附加信息。 Version
类不包含直接获取附加信息的方法。 它包含一个version()
方法,该方法返回List<Integer>
,其中列表包含版本号的所有元素。 列表中的前三个元素是$major
,$minor
和$security
。 其余元素包含附加版本号信息。
Runtime.Version
类包含在次序和等式方面比较两个版本字符串的方法。 可以比较它们或者不包含可选的构建信息($opt
)。 这些比较方法如下:
int compareTo(Version v) int compareToIgnoreOptional(Version v) boolean equals(Object v) boolean equalsIgnoreOptional(Object v)
如果v1小于等于或大于v2,表达式v1.compareTo(v2)
将返回负整数,零或正整数。 compareToIgnoreOptional()
方法的工作方式与compareTo()
方法相同,只不过它在比较时忽略了可选的构建信息。 equals()
和equalsIgnoreOptional()
方法将两个版本字符串进行比较,不包含可选构建信息。
哪个版本的字符串代表最新版本:9.1.1或9.1.1-ea? 第一个不包含预发行元素,而第二个字符串包含,所以第一个是最新版本。 哪个版本的字符串代表最新版本:9.1.1或9.1.1.1-ea? 这一次,第二个代表最新的版本。 比较发生在序列$vnum
,$pre
,$build
和$opt
。 当版本号较大时,不比较版本字符串中的其他元素。
此部分的源代码位于名为com.jdojo.version.string的模块中,其声明如下所示。
// module-info.javamodule com.jdojo.version.string { exports com.jdojo.version.string; }
下面代码包含一个完整的程序,显示如何使用Runtime.Version
类来提取版本字符串的所有部分。
com.jdojo.version.string// VersionTest.javapackage com.jdojo.version.string; import java.util.List; import java.lang.Runtime.Version;public class VersionTest { public static void main(String[] args) { String[] versionStrings = { "9", "9.1", "9.1.2", "9.1.2.3.4", "9.0.0", "9.1.2-ea+153", "9+132", "9-ea+132-2016-08-23", "9+-123", "9.0.1-ea+132-2016-08-22.10.56.45am"}; for (String versonString : versionStrings) { try { Version version = Version.parse(versonString); // Get the additional version number elements // which start at 4th element String vnumAdditionalInfo = getAdditionalVersionInfo(version); System.out.printf("Version String=%s%n", versonString); System.out.printf("Major=%d, Minor=%d, Security=%d, Additional Version=%s," + " Pre=%s, Build=%s, Optional=%s %n%n", version.major(), version.minor(), version.security(), vnumAdditionalInfo, version.pre().orElse(""), version.build().isPresent() ? version.build().get().toString() : "", version.optional().orElse("")); } catch (Exception e) { System.out.printf("%s%n%n", e.getMessage()); } } } // Returns the version number elements from the 4th elements to the end public static String getAdditionalVersionInfo(Version v) { String str = ""; List<Integer> vnum = v.version(); int size = vnum.size(); if (size >= 4) { str = str + String.valueOf(vnum.get(3)); } for (int i = 4; i < size; i++) { str = str + "." + String.valueOf(vnum.get(i)); } return str; } }
VersionTest
类,显示如何使用Runtime.Version
类来处理版本字符串。
下面是输出结果:
Version String=9Major=9, Minor=0, Security=0, Additional Version=, Pre=, Build=, Optional= Version String=9.1Major=9, Minor=1, Security=0, Additional Version=, Pre=, Build=, Optional= Version String=9.1.2Major=9, Minor=1, Security=2, Additional Version=, Pre=, Build=, Optional= Version String=9.1.2.3.4Major=9, Minor=1, Security=2, Additional Version=3.4, Pre=, Build=, Optional= Invalid version string: '9.0.0'Version String=9.1.2-ea+153Major=9, Minor=1, Security=2, Additional Version=, Pre=ea, Build=153, Optional= Version String=9+132Major=9, Minor=0, Security=0, Additional Version=, Pre=, Build=132, Optional= Version String=9-ea+132-2016-08-23Major=9, Minor=0, Security=0, Additional Version=, Pre=ea, Build=132, Optional=2016-08-23Version String=9+-123Major=9, Minor=0, Security=0, Additional Version=, Pre=, Build=, Optional=123Version String=9.0.1-ea+132-2016-08-22.10.56.45am Major=9, Minor=0, Security=1, Additional Version=, Pre=ea, Build=132, Optional=2016-08-22.10.56.45am
二. JDK和JRE的改变
JDK和JRE已经在Java SE 9中进行了模块化处理。对结构进行了一些修改。 还进行了一些其他更改,以提高性能,安全性和可维护性。 大多数这些变化会影响类库开发人员和IDE开发人员,而不是应用程序开发人员。为了讨论这些变化,把它们分为三大类:
布局变化
行为变化
API更改
以下部分将详细介绍这些改变。
1. JDK和JRE的布局变化
结构更改会影响运行时映像中的目录和文件的组织方式,并影响其内容。 在Java SE 9之前,JDK构建系统用于生成两种类型的运行时映像 ——Java运行时环境(JRE)和Java开发工具包(JDK)。 JRE是Java SE平台的完整实现,JDK包含了JRE和开发工具和类库。 可下图显示了Java SE 9之前的JDK安装中的主目录。JDK_HOME是安装JDK的目录。 如果你只安装了JRE,那么你只有在jre目录下的目录。
在 Java SE 9之前,JDK中:
bin目录用于包含命令行开发和调试工具,如javac,jar和javadoc。 它还用于包含Java命令来启动Java应用程序。
include目录包含在编译本地代码时使用的C/C++头文件。
lib目录包含JDK工具的几个JAR和其他类型的文件。 它有一个tools.jar文件,其中包含javac编译器的Java类。
jre\bin目录包含基本命令,如java命令。 在Windows平台上,它包含系统的运行时动态链接库(DLL)。
jre\lib目录包含用户可编辑的配置文件,如.properties和.policy文件。
jre\lib\approved目录包含允许使用标准覆盖机制的JAR。 这允许在Java社区进程之外创建的实施标准或独立技术的类和接口的更高版本被并入Java平台。 这些JAR被添加到JVM的引导类路径中,从而覆盖了Java运行时中存在的这些类和接口的任何定义。
jre\lib\ext目录包含允许扩展机制的JAR。 该机制通过扩展类加载器(该类加载器)加载了该目录中的所有JAR,该引导类加载器是系统类加载器的子进程,它加载所有应用程序类。 通过将JAR放在此目录中,可以扩展Java SE平台。 这些JAR的内容对于在此运行时映像上编译或运行的所有应用程序都可见。
jre\lib目录包含几个JAR。 rt.jar文件包含运行时的Java类和资源文件。 许多工具依赖于rt.jar文件的位置。
jre\lib目录包含用于非Windows平台的动态链接本地库。
jre\lib目录包含几个其他子目录,其中包含运行时文件,如字体和图像。
JDK和JRE的根目录包含多个文件,如COPYRIGHT,LICENSE和README.html。 根目录中的发行文件包含一个描述运行时映像(如Java版本,操作系统版本和体系结构)的键值对。 以下代码显示了JDK 8中的示例版本文件的部分内容:
JAVA_VERSION="1.8.0_66"OS_NAME="Windows"OS_VERSION="5.2"OS_ARCH="amd64"BUILD_TYPE="commercial"
Java SE 9调整了JDK的目录层次结构,并删除了JDK和JRE之间的区别。 下图显示了Java SE 9中JDK安装的目录。JDK 9中的JRE安装不包含include和jmods目录。
在Java SE 9 的JDK中:
没有名为jre的子目录。
bin目录包含所有命令。 在Windows平台上,它继续包含系统的运行时动态链接库。
conf目录包含用户可编辑的配置文件,例如以前位于jre\lib目录中的.properties和.policy文件。
include目录包含要在以前编译本地代码时使用的C/C++头文件。 它只存在于JDK中。
jmods目录包含JMOD格式的平台模块。 创建自定义运行时映像时需要它。 它只存在于JDK中。
legal 目录包含法律声明。
lib目录包含非Windows平台上的动态链接本地库。 其子目录和文件不应由开发人员直接编辑或使用。
JDK 9的根目录有如COPYRIGHT和README等文件。 JDK 9中的发行文件包含一个带有MODULES键的新条目,其值为映像中包含的模块列表。 JDK 9映像中的发行文件的部分内容如下所示:
MODULES=java.rmi,jdk.jdi,jdk.policytoolOS_VERSION="5.2"OS_ARCH="amd64"OS_NAME="Windows"JAVA_VERSION="9"JAVA_FULL_VERSION="9-ea+133"
在列表中只显示了三个模块。 在完整的JDK安装中,此列表将包括所有平台模块。 在自定义运行时映像中,此列表将仅包含你在映像中使用的模块。
Tips
JDK中的lib\tools.jar和JRE中的lib\rt.jar已从Java SE 9中删除。这些JAR中可用的类和资源现在以文件中的内部格式存储在lib目录的命名模块中。 可以使用称为jrt的新方案来从运行时映像检索这些类和资源。 依靠这些JAR位置的应用程序将不再工作。
2. 行为变化
行为变化将影响应用程序的运行时行为。 以下部分将说明这些更改。
三. 支持标准覆盖机制
在Java SE 9之前,可以使用支持标准的覆盖机制来使用更新版本的类和接口来实现支持标准或独立API,如javax.rmi.CORBA包和Java API for XML Processing(JAXP) ,它们是在Java社区进程之外创建的。 http://www.cnblogs.com/IcanFixIt/p/7131676.html