Java 9 揭秘(8. JDK 9重大变动)

Tips
做一个毕生学习的口。

图片 1

当本章,主要介绍以下内容:

  • 新的JDK版本控制方案是什么
  • 哪利用Runtime.Version类解析JDK版本字符串
  • JDK JRE 9的新目录布局是什么
  • JDK 9中的批注的正经覆盖机制如何行事的
  • 在JDK 9中应用扩展机制的生成
  • JDK 9中的类似加载器如何工作与模块的加载方式
  • 资源如何封装于JDK 9中之模块中
  • 何以行使ModuleClassClassLoader看似中之资源查找方法访问模块中的资源
  • 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.java
module com.jdojo.version.string {
    exports com.jdojo.version.string;
}

下面代码包含一个完的次序,显示怎么采取Runtime.Version仿佛来提取版本字符串的持有片。

com.jdojo.version.string
// VersionTest.java
package 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=9
Major=9, Minor=0, Security=0, Additional Version=, Pre=, Build=, Optional=
Version String=9.1
Major=9, Minor=1, Security=0, Additional Version=, Pre=, Build=, Optional=
Version String=9.1.2
Major=9, Minor=1, Security=2, Additional Version=, Pre=, Build=, Optional=
Version String=9.1.2.3.4
Major=9, Minor=1, Security=2, Additional Version=3.4, Pre=, Build=, Optional=
Invalid version string: '9.0.0'
Version String=9.1.2-ea+153
Major=9, Minor=1, Security=2, Additional Version=, Pre=ea, Build=153, Optional=
Version String=9+132
Major=9, Minor=0, Security=0, Additional Version=, Pre=, Build=132, Optional=
Version String=9-ea+132-2016-08-23
Major=9, Minor=0, Security=0, Additional Version=, Pre=ea, Build=132, Optional=2016-08-23
Version String=9+-123
Major=9, Minor=0, Security=0, Additional Version=, Pre=, Build=, Optional=123
Version 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目录下之目录。

图片 2

在 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目录。

图片 3

在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.policytool
OS_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社区进程之外创造的。
这些JAR已经让填补加至JVM的引类路径中,从而覆盖了JRE中是的这些近似以及接口的其他概念。
这些JAR的位置由称为也java.endorsed.dirs的体系特性指定,其中目录由特定于阳台的路径分隔符字符分隔。
如果不安装此属性,则运行时用于jre\lib\approved目录中查找JAR。

Java SE 9仍然支撑认可的业内与独立API覆盖机制。 在Java SE
9中,运行时映像由模块组成。
要使用是编制,需要动用更新版本的模块,用于支持标准与独立API。
需要以--upgrade-module-path命行选项。
此选项的价是含有“承认标准”和“独立API”模块的目列表。
Windows上的以下命令将覆盖“标准规范”模块,如JDK
9中的java.corba模块。将使用umod1和umod2目录中之模块而休是运行时映像受之附和模块:

java --upgrade-module-path umod1;umod2 <other-options>

Tips
于Java SE
9中,创建一个JAVA_HOME\lib\approvaled目录并设置名也java.endorsed.dirs的体系特性,会发错误。

四. 扩展机制

版本9之前的Java
SE允许扩展机制,可以由此以JAR放置于系统性能java.ext.dirs指定的目录中来扩充运行时映像。
如果非设置是网性能,则使jre\lib\ext目录作为其默认值。
该机制通过扩展类加载器(这是指引类加载器的子类)和网类加载器的父级加载了该目录中之具备JAR。
它加载所有应用程序类。
这些JAR的始末对在此运行时映像上编译或运行的有着应用程序都可见。

Java SE 9不支持扩大机制。
如果欲接近的效用,可以用这些JAR放在类路径的前头。
使用名吧JAVA_HOME\lib\ext的目录或安装名吧java.ext.dirs的系性能会招JDK
9中的一无是处。

1. 类加载器的转移

在程序运行时,每个类别且是因为类似加载器加载,该类由java.lang.ClassLoader类的一个实例表示。
如果你发一个对象引用obj,你可以通过调用obj.getClass().getClassLoader()办法赢得它们的好像加载器引用。
可以应用其getParent()术获得类加载器的父类。

以版本9之前,JDK用三独八九不离十加载器来加以载类,如下图所出示。
图中箭头方向表示委托方向。
可以长更多之类加载器,这是ClassLoader类的子类。
来自不同职务以及档次的JDK加载类吃的老三单类似加载器。

图片 4

JDK类加载器以隔开方式工作 —— 引导类加载器位于层次结构的顶部。
类加载器将类似加载请求委托为上层类加载器。
例如,如果应用程序类加载器需要加载一个近乎,它以请求委托为扩展类加载器,扩展类加载器又将请委托给带类加载器。
如果引导类加载器无法加载类,扩展类加载器将尝试加载它。
如果扩展类加载器无法加载类,则应用程序类加载器尝试加载它。
如果应用程序类加载器无法加载它,则抛出ClassNotFoundException异常。

带类加载器是扩张类加载器的父类。
扩展类加载器是应用程序类加载器的父类。 引导类加载器没有父类。
默认情况下,应用程序类加载器将凡若创造的别类加载器的父类。

引导类加载器加载由Java平台组成的引导类,包括JAVA_HOME\lib\rt.jar中之类似以及任何几单运行时JAR。
它完全在虚拟机中落实。
可以行使-Xbootclasspath/p-Xbootclasspath/a命令行选项来附加引导目录。
可以用-Xbootclasspath选取指定引导类路径,该选择将替换默认的带类路径。
在运作时,sun.boot.class.path系统特性包含引导类路径的才读值。
JDK通过null表示此看似加载器。 也就是说,你切莫能够获她的援。
例如,Object看似由带类加载器加载,并且Object.class.getClassLoade()表达式将回null。

壮大类加载器用于通过java.ext.dirs系统性能指定的目录中的在JAR中之恢弘机制加载可用之好像。要得到扩展类加载器的援,需要得到应用程序类加载器的援,并于该引用上利用getParent()方法。

应用程序类加载器从由于CLASSPATH环境变量指定的应用程序类路径或指令行选项-cp-classpath加载类。应用程序类加载器也叫系统类加载器,这是相同栽误称,它暗示其加载系统类。可以使ClassLoader类似的静态方法getSystemClassLoader()博对应用程序类加载器的援。

JDK
9保持三级分层类加载器架构以落实向后相当。但是,从模块系统加载类的点子来一对浮动。
JDK 9近似加载器层次结构如下图所示。

图片 5

告小心,在JDK
9中,应用程序类加载器可以委托给平台类加载器以及引导类加载器;平台类加载器可以委托给带类加载器和应用程序类加载器。
以下详细介绍JDK 9近似加载器的做事原理。

在JDK 9中,引导类加载器是由类库和代码在虚拟机中落实之。
为了向后相当,它当程序中依然由null表示。
例如,Object.class.getClassLoader()照例返回null。
但是,并无是颇具的Java SE平台和JDK模块都是因为带类加载器加载。
举几单例,引导类加载器加载的模块是java.basejava.loggingjava.prefsjava.desktop
其他Java
SE平台和JDK模块由平台类加载器和应用程序类加载器加载,这在脚介绍。 JDK
9中不再支持用于指定引导类路径,-Xbootclasspath-Xbootclasspath/p选择和系统性能sun.boot.class.path-Xbootclasspath/a选取仍然为支持,其值存储在jdk.boot.class.path.append的系性能被。

JDK 9不再支持扩大机制。
但是,它以扩大类加载器保留在叫也平台类加载器的新名称下。
ClassLoader接近富含一个叫做也getPlatformClassLoader()的静态方法,该措施返回对平台类加载器的援。
下表包含平台类加载器加载的模块列表。 平台类加载器用于其他一样目的。
默认情况下,由带类加载器加载的类将被予以所有权力。
但是,几只类似非需具备权力。 这些看似在JDK
9中已经于撤销了特权,并且它吃平台类加载器加载以增进安全性。

下是JDK 9中由平台加载器加载的模块列表。

java.activation
java.xml.ws.annotation
jdk.desktop
java.compiler
javafx.base
jdk.dynalink
java.corba
javafx.controls
jdk.javaws
java.jnlp
javafx.deploy
jdk.jsobject
java.scripting
javafx.fxml
jdk.localedata
java.se
javafx.graphics
jdk.naming.dns
java.se.ee
javafx.media
jdk.plugin
java.security.jgss
javafx.swing
jdk.plugin.dom
java.smartcardio
javafx.web
jdk.plugin.server
java.sql
jdk.accessibility
jdk.scripting.nashorn
java.sql.rowset
jdk.charsets
jdk.security.auth
java.transaction
jdk.crypto.cryptoki
jdk.security.jgss
java.xml.bind
jdk.crypto.ec
jdk.xml.dom
java.xml.crypto
jdk.crypto.mscapi
jdk.zipfs
java.xml.ws
jdk.deploy

应用程序类加载器加载在模块路径上找到的应用程序模块和一些资工具要导出工具API的JDK模块,如下表所示。
仍然可应用ClassLoader类的getSystemClassLoader()的静态方法来获得应用程序类加载器的援。

jdk.attach
jdk.jartool
jdk.jstatd
jdk.compiler
jdk.javadoc
jdk.pack
jdk.deploy.controlpanel
jdk.jcmd
jdk.packager
jdk.editpad
jdk.jconsole
jdk.packager.services
jdk.hotspot.agent
jdk.jdeps
jdk.policytool
jdk.internal.ed
jdk.jdi
jdk.rmic
jdk.internal.jvmstat
jdk.jdwp.agent
jdk.scripting.nashorn.shell
jdk.internal.le
jdk.jlink
jdk.xml.bind
jdk.internal.opt
jdk.jshell
jdk.xml.ws

Tips
每当JDK
9之前,扩展类加载器和应用程序类加载器是java.net.URLClassLoader仿佛的一个实例。
在JDK
9中,平台类加载器(以前的扩张类加载器)和应用程序类加载器是内JDK类的实例。
如果你的代码依赖于·URLClassLoader·类的一定措施,代码可能会见于JDK
9中夭折。

JDK 9中的类似加载机制有变更。 三独放的类加载器一起合作来加以载类。
当应用程序类加载器需要加载类时,它以寻找定义及持有类加载器的模块。
如果生适量的模块定义在这些近似加载器中,则该类加载器将加载类,这代表应用程序类加载器现在足寄给带类加载器和平台类加载器。
如果当呢这些类似加载器定义之命名模块中觅不交类似,则应用程序类加载器用委托给那父类,即平台类加载器。
如果类似没有加载,则应用程序类加载器以寻找类似路径。
如果它于接近路径中找到类似,它将作该未命名模块的成员加载该类。
如果当近似路径中搜索不交类似,则抛出ClassNotFoundException异常。

当平台类加载器需要加载类时,它用追寻定义及独具类加载器的模块。
如果一个相宜的模块于定义也这些类似加载器中,则该类加载器加载该类。
这意味平台类加载器可以寄给带类加载器以及应用程序类加载器。
如果当为这些看似加载器定义之命名模块中追寻不至一个像样,那么平台类加载器将委托为它的父类,即引导类加载器。

当引导类加载器需要加载一个类时,它见面找自己之命名模块列表。
如果搜索不交类似,它以透过命令行选项-Xbootclasspath/a指定的文本及目录列表进行搜寻。
如果它们以带类路径上找到一个好像,它以当其不命名模块的积极分子加载该类。

你可以望类加载器及其加载的模块和类。 JDK
9包含一个称吧-Xlog::modules的选择项,用于在虚拟机加载时记下调试要跟消息。
其格式如下:

-Xlog:modules=<debug|trace>

此选项有大量的输出。 建议用出口重定向到一个文本,以便可以轻松查看。
以下命令在Windows上运行素数检查的客户端程序,并当test.txt文件被记录模块加载信息。
下面显示有输出。 输出显示定义模块的类加载器。
命令:

C:\Java9Revealed>java -Xlog:modules=trace --module-path lib
 --module com.jdojo.prime.client/com.jdojo.prime.client.Main > test.txt

一些音输出:

[0.022s][trace][modules] Setting package: class: java.lang.Object, package: java/lang, loader: <bootloader>, module: java.base
[0.022s][trace][modules] Setting package: class: java.io.Serializable, package: java/io, loader: <bootloader>, module: java.base
...
[0.855s][debug][modules] define_module(): creation of module: com.jdojo.prime.client, version: NULL, location: file:///C:/Java9Revealed/lib/com.jdojo.prime.client.jar, class loader 0x00000049ec86dd90 a 'jdk/internal/loader/ClassLoaders$AppClassLoader'{0x00000000895d1c98}, package #: 1
[0.855s][trace][modules] define_module(): creation of package com/jdojo/prime/client for module com.jdojo.prime.client
...

五. 访问资源

资源是应用程序使用的数据,例如图像,音频,视频,文本文件等。Java提供了同种通过以类似路径上一贯资源来走访资源的职务无关之计。
需要为同在JAR中打包类文件一律之措施打包资源,并以JAR添加到近似路径。
通常,类公事以及资源打包在和一个JAR中。
访问资源是每个Java开发人员执行之首要任务。
在连片下去的章节中,将以版本9和JDK 9之前说JDK中提供可用的API。

1. 每当JDK 9之前看资源

当本节遇,将分解什么以版本9之前以JDK中做客资源。如果你就清楚怎么样在版本9之前看JDK中之资源,可以超越到下一致节省,介绍如何看JDK
9中之资源。

在Java代码中,资源由资源名称标识,资源名称是出于斜线(/)分隔的一律拧字符串。
对于仓储在JAR中的资源,资源名称仅仅是储存于JAR中的文本的途径。
例如,在JDK
9之前,存储于rt.jar中之java.lang包吃的Object.class文本是一个资源,其资源名称是java/lang/Object.class。

于JDK 9之前,可以采取以下简单单近乎吃的艺术来拜访资源:

java.lang.Class
java.lang.ClassLoader

资源由ClassLoader定位。
一个Class代办中的资源寻找办法及它们的ClassLoader
因此,一旦了解ClassLoader运用的资源加载过程,将非会见于采用Class接近的主意时相遇问题。
在少数单近乎吃起少数种植不同之命名实例方法:

URL getResource(String name)
InputStream getResourceAsStream(String name)

有限栽方式都见面因为相同的道找到资源。 它们的异样就在返回路。
第一独道返回一个URL,而第二个措施返回一个InputStream
第二栽办法相当给调用第一种植艺术,然后在返回的URL目标及调用openStream()

Tips
倘若搜索不交指定的资源,所有资源查找方法还拿回到null。

ClassLoader类似富含三个附加的寻资源的静态方法:

static URL getSystemResource(String name)
static InputStream getSystemResourceAsStream(String name)
static Enumeration<URL> getSystemResources(String name)

这些艺术以系统类加载器(也称应用程序类加载器)来探寻资源。
第一种方法返回找到的率先只资源的URL
第二种植艺术返回找到的率先个资源的InputStream
第三栽方法返回下指定的资源名称找到的富有资源的URL枚举。

苟找到资源,有半点种植类型的点子可以自——getSystemResource *getResource *屡遭展开精选。
在谈论哪种方式是极度好的前头,重要的凡若了解有少栽档次的资源:

  • 系统资源
  • 无系统资源

卿得了解她们中的别,以询问资源查找体制。系统资源是以bootstrap类路径,扩展目录中的JAR和应用程序类路径中找到的资源。非系统资源可以储存在除路径之外的职,例如当一定目录,网络直达或者数据库被。
getSystemResource()方运用应用程序类加载程序找到一个资源,委托为它的父类,它是扩张类加载器,后者同时寄托为其的父类(引导类加载器)。如果您的应用程序是单独的应用程序,并且它才使用三只放置的JDK类加载器,那么你拿特别好的行使名也getSystemResource *的静态方法。它将当类似路径中找到有资源,包括运转时映像中之资源,如rt.jar文件。如果你的应用程序是在浏览器中运作的多少程序,或当应用程序服务器和Web服务器中运作的企业应用程序,则答应采用名吧getResource*的实例方法,它可利用一定的接近加载器来查找资源。如果在Class目标上调用getResource*方,则会采取时接近加载器(加载Class目标的近乎加载器)来搜寻资源。

传递给ClassLoader好像中所有办法的资源名称还是纯属的,它们不因为斜线(/)开头。
例如,当调用ClassLoadergetSystemResource()方法时,将动用java/lang/Object.class作为资源名称。

Class好像吃之资源查找方法可指定绝对跟相对资源名称。
绝对资源名称为斜线开头,而相对资源名称不用。
当使用绝对化名称时,Class类似吃的方法会删除前导斜线并委派给加载Class靶的切近加载器来查找资源。
以下调用

Test.class.getResource("/resources/test.config");
会晤于换成为
Test.class.getClassLoader().getResource("resources/test.config");

当以相对名称时,Class类似吃的法预先添加了包号,在用斜线后以及斜线替换包名中之触发,然后又托付加载Class对象的类似加载器来寻找资源。
假设测试类在com.jdojo.test包中,以下调用:
Test.class.getResource("resources/test.config");
会晤受转移成
Test.class.getClassLoader() .getResource("com/jdojo/test/resources/test.config");

咱来拘禁一个当JDK 9之前找资源的事例。 使用JDK
8运行示例。NetBeans项目名为吧com.jdojo.resource.preJDK9。
如果您创造自己之品类,请保管以项目之Java平台跟源更改为JDK
8。类与资源的排如下:
word_to_number.properties
com/jdojo/resource/prejdk9/ResourceTest.class
com/jdojo/resource/prejdk9/resources/number_to_word.properties

拖欠型包含两单资源文件:根目录下的word_to_number.properties和com/jdojo/resource/prejdk9/resources目录中的number_to_word.properties。
这有限独特性文件之始末分别如下所示:

One=1
Two=2
Three=3
Four=4
Five=5

1=One
2=Two
3=Three
4=Four
5=Five

脚包含一个一体化的次序,显示怎么以不同之类似及其方法寻找资源。
该程序演示了可以应用程序中之好像公事用作资源,可以动用相同的方法找到其来探寻其他类的资源。

// ResourceTest.java
package com.jdojo.resource.prejdk9;
import java.io.IOException;
import java.net.URL;
import java.util.Properties;
public class ResourceTest {
    public static void main(String[] args) {
        System.out.println("Finding resources using the system class loader:");
        findSystemResource("java/lang/Object.class");
        findSystemResource("com/jdojo/resource/prejdk9/ResourceTest.class");
        findSystemResource("com/jdojo/prime/PrimeChecker.class");
        findSystemResource("sun/print/resources/duplex.png");
        System.out.println("\nFinding resources using the Class class:");
        // A relative resource name - Will not find Object.class
        findClassResource("java/lang/Object.class");
        // An absolute resource name - Will find Object.class
        findClassResource("/java/lang/Object.class");
        // A relative resource name - will find the class
        findClassResource("ResourceTest.class");
        // Load the wordtonumber.properties file
        loadProperties("/wordtonumber.properties");
        // Will not find the properties because we are using
        // an absolute resource name
        loadProperties("/resources/numbertoword.properties");
        // Will find the properties
        loadProperties("resources/numbertoword.properties");
    }
    public static void findSystemResource(String resource) {
        URL url = ClassLoader.getSystemResource(resource);
        System.out.println(url);
    }
    public static URL findClassResource(String resource) {
        URL url = ResourceTest.class.getResource(resource);
        System.out.println(url);
        return url;
    }
    public static Properties loadProperties(String resource) {
        Properties p1 = new Properties();
        URL url = ResourceTest.class.getResource(resource);
        if (url == null) {
            System.out.println("Properties not found: " + resource);
            return p1;
        }
        try {
            p1.load(url.openStream());
            System.out.println("Loaded properties from " + resource);
            System.out.println(p1);
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
        return p1;
    }
}

以下是出口结果:

Finding resources using the system class loader:
jar:file:/C:/java8/jre/lib/rt.jar!/java/lang/Object.class
file:/C:/Java9Revealed/com.jdojo.resource.prejdk9/build/classes/com/jdojo/resource/prejdk9/ResourceTest.class
null
jar:file:/C:/java8/jre/lib/resources.jar!/sun/print/resources/duplex.png
Finding resources using the Class class:
null
jar:file:/C:/java8/jre/lib/rt.jar!/java/lang/Object.class
file:/C:/Java9Revealed/com.jdojo.resource.prejdk9/build/classes/com/jdojo/resource/prejdk9/ResourceTest.class
Loaded properties from /wordtonumber.properties
{One=1, Three=3, Four=4, Five=5, Two=2}
Properties not found: /resources/numbertoword.properties
Loaded properties from resources/numbertoword.properties
{5=Five, 4=Four, 3=Three, 2=Two, 1=One}

2. 以JDK 9 中走访资源

在JDK 9之前,可以从类路径上之其它JAR访问资源。 在JDK
9中,类及资源封装于模块中。 在率先不行尝试被,JDK
9设计人员强制执行模块封装规则,模块中之资源要对该模块是私有的,因此它们只能以拖欠模块内的代码中访问。
虽然这规则以答辩及看起格外好,但是于超过模块共享资源的框架和加载的类似公事作为来自其它模块的资源,就会带来问题。
为了少地走访模块中之资源,做了有的投降,但是依旧强制执行模块的包。
JDK 9包含三近乎资源查找方法:

java.lang.Class
java.lang.ClassLoader
java.lang.Module

ClassClassLoader恍如没有新增任何新的方式。
Module接近富含一个getResourceAsStream(String name)计,如果找到该资源,返回一个InputStream;否则回null。

六. 资源命名语法

资源采取由斜线分隔的字符串序列命名,例如com/jdojo/states.png,/com/jdojo/words.png和logo.png。
如果资源名称为斜线开头,则吃视为绝对资源名称。

以以下规则从资源名称被估算包(package)的称谓:

  • 假使资源名称为斜线开头,删除第一只斜线。
    例如,对于资源名称/com/jdojo/words.png,此步骤将导致com/jdojo/words.png。
  • 自最后一个斜线开始去资源名称被的具有字符。
    在斯事例中,com/jdojo/words.png导致com/jdojo。
  • 故此点号(.)替换名称中之每个剩余的斜线。
    所以,com/jdojo被转移成com.jdojo。 生成的字符串是保号。

聊情况下利用这些手续会导致一个无命名的包还是一个失效的管号。
包名称(如果存在)必须由中之Java标识符组成。
如果没有保证号,它深受称之为未命名的保险。 例如,将META-INF/resource
/logo.png视为资源名称。
应用上同样组规则,其担保号将吃计算呢“META-INF.resources”,它不是行得通的包名,但它是资源的有效性路径。

七. 查找资源的规则

是因为向后兼容性及对模块系统的强封装的应允,JDK
9中找找资源的初规则是扑朔迷离的,基于以下几单因素:

  • 包含资源的模块类型:命名的,开放之,未命名的要活动命名的模块;
  • 正在访问资源的模块:它是和一个模块还是另外一个模块?
  • 方为拜的资源的管号:它是不是是行得通Java包? 这是一个不命名的保管?
  • 包装包含资源的保险:将涵盖资源的包导出,打开或包装到走访资源的模块?
  • 正在访问的资源的公文扩展名:资源是.class文件或者其他种类的文书?
  • 方使用啊类的计来聘资源:ClassClassLoaderModule类?

以下规则适用于含有资源的命名模块:

  • 而资源名称以.class结尾,则可通过其他模块中的代码访问资源。
    也就是说,任何模块都可看任何命名模块中的切近公事。
  • 若果从资源名称计算的保证号不是实惠的Java包号,例如META-INF.resources,则好透过外模块中之代码访问该资源。
  • 倘若打资源名称计算的承保号是未命名的管教,例如对于资源名称(如word.png),则足以由此任何模块中的代码访问该资源。
  • 比方带有该资源的软件包对访问该资源的模块开放,则资源可以经过该模块中之代码访问。
    一个包对模块开放,因为定义包之模块是一个开放之模块,或者模块打开所有其他模块的保,或者模块只使用一个范围的开辟语句打开包。
    如果没有因任何这些方式打开包,则该包中之资源不能够叫该模块外的代码访问。
  • 是规则是达一个平整的子。
    打开未命名,自动或放模块中之每个包,因此具有其他模块中之代码都可看这些模块中的保有资源。

Tips
命名模块中之保证要打开,而未是导出,以访该资源。
导出一个模块的保允许其他模块访问该包中之公物项目(而非是资源)。

当看命名模块中之资源时,ModuleClassClassLoader恍如吃的各种资源查找方法的一言一行有所不同:

  • 得动用Module类的getResourceAsStream()办法来拜会模块中的资源。
    此方法是调用方敏感的。
    如果调用者模块不同,则这措施以下具有资源而访问性规则,如上所述。
  • 当指定模块中定义之近乎的Class类中的getResource *()措施就在拖欠命名模块中一贯资源。
    也就是说,不克采取这些措施来定位定义调用这些艺术的切近的命名模块之外的近乎。
  • ClassLoader类中的getResource *()道基于前描述的规则列表来恒定命名模块中之资源。
    这些艺术无是调用者敏感的。
    在尝试寻找资源本身之前,类加载器将资源搜索委托为那个父类。
    这些办法来少数只例外:1)它们只在无偿打开的包中定位资源。
    如果应用范围的打开语句打开包,则这些方式将非会见当这些包中找到资源。
    2)它们寻找于近似加载器中定义之模块。

Class目标将独自以其所属的模块中找到资源。
它还支持为斜线开头的绝对资源名称,以及非因为斜线开头的相对资源名称。
以下是采用Class靶的几乎单示范:

// Will find the resource
URL url1 = Test.class.getResource("Test.class");
// Will not find the resource because the Test and Object classes are in different modules
URL url2 = Test.class.getResource("/java/lang/Object.class");
// Will find the resource because the Object and Class classes are in the same module, java.base
URL url3 = Object.class.getResource("/java/lang/Class.class");
// Will not find the resource because the Object class is in the java.base module whereas
// the Driver class is in the java.sql module
URL url4 = Object.class.getResource("/java/sql/Driver.class");

使用Module好像定位资源要持有该模块的援。
如果得以看该模块中的切近,则在拖欠Class目标及应用getModule()主意为有了模块引用。
这是赢得模块引用的卓绝简便易行方法。
有时候,你管模块名称作为字符串,而非是拖欠模块中之类似的援。
可以起模块名称被找到模块引用。
模块于集团改为是因为java.lang包吃的ModuleLayer接近的实例表示的重合。
JVM至少含有一个boot 层。 boot层中的模块映射到放的好像加载器 ——
引导类加载器,平台类加载器和应用程序类加载器。
可以用ModuleLayer类的boot()静态方法获取boot层的援:

// Get the boot layer
ModuleLayer bootLayer = ModuleLayer.boot();

假若取得boot层的援,可以使那findModule(String moduleName)艺术获得模块的援:

// Find the module named com.jdojo.resource in the boot layer
Optional<Module> m = bootLayer.findModule("com.jdojo.resource");
// If the module was found, find a resource in the module
if(m.isPresent()) {
    Module testModule = m.get();
    String resource = "com/jdojo/resource/opened/opened.properties";
    InputStream input = module.getResourceAsStream(resource);
    if (input != null) {
        System.out.println(resource + " found.");
    } else {
        System.out.println(resource + " not found.”);
    }
} else {
    System.out.println("Module com.jdojo.resource does not exist");
}

八. 走访命名模块中之资源的示范

以以有的被,将见到资源查找规则之实际经过。
在com.jdojo.resource的模块中封装资源,其宣称如下所示。

// module-info.java
module com.jdojo.resource {
    exports com.jdojo.exported;
    opens com.jdojo.opened;
}

拖欠模块导出com.jdojo.exported包,并开拓com.jdojo.opened包。

以下是com.jdojo.resource模块中具有文件的列表:

  • module-info.class
  • unnamed.properties
  • META-INF\invalid_pkg.properties
  • com\jdojo\encapsulated\encapsulated.properties
  • com\jdojo\encapsulated\EncapsulatedTest.class
  • com\jdojo\exported\AppResource.class
  • com\jdojo\exported\exported.properties
  • com\jdojo\opened\opened.properties
  • com\jdojo\opened\OpenedTest.class

发四个像样公事。 在是事例中,只有module-info.class文件特别重要。
其他类公事定义一个无其余细节的同名的类似。
具有.properties扩展名的有着文件还是资源文件,其情以是示例中无紧要。
源代码包含Java9Revealed\com.jdojo.resource目录中这些文件之始末。

Tips
源代码在com.jdojo.resource

unnamed.properties文件于无命名的包中,因此好由此任何其他模块中之代码来定位。
invalid_pkg.properties文件在META-INF目录中,它不是实惠之Java包号,因此该文件呢可以经另外其它模块中之代码来定位。
com.jdojo.encapsulated包没有打开,所以encapsulated.properties文件未能够经过其他模块中的代码来找到。
com.jdojo.exported包未打开,所以export.properties文件未可知由此另外模块中的代码来找到。
com.jdojo.opened包是开拓的,所以opened.properties文件可以通过其他模块中之代码来恒定。该模块中的备类似公事可以透过外模块中之代码来恒定。

下清单包含com.jdojo.resource.test模块的模块声明。本模块中之代码用尝试看com.jdojo.resource模块中的资源和本模块被的资源。你要用com.jdojo.resource模块添加到此模块路径为拓展编译。
在 NetBean
IDE中com.jdojo.resource.test项目之属性对话框而下图所显示。它将com.jdojo.resource模块添加到那模块路径。

图片 6

// module-info.java
module com.jdojo.resource.test {
    requires com.jdojo.resource;
    exports com.jdojo.resource.test;
}

com.jdojo.resource.test模块中之公文随如下方式排列:

  • module-info.class
  • com\jdojo\resource\test\own.properties
  • com\jdojo\resource\test\ResourceTest.class

欠模块包含名为也own.properties的资源文件,该文件在com.jdojo.resource.test包中。
own.properties文件也空。 下面包含ResourceTest接近的代码。

// ResourceTest
package com.jdojo.resource.test;
import com.jdojo.exported.AppResource;
import java.io.IOException;
import java.io.InputStream;
public class ResourceTest {
    public static void main(String[] args) {
        // A list of resources
        String[] resources = {
            "java/lang/Object.class",
            "com/jdojo/resource/test/own.properties",
            "com/jdojo/resource/test/ResourceTest.class",
            "unnamed.properties",
            "META-INF/invalid_pkg.properties",
            "com/jdojo/opened/opened.properties",
            "com/jdojo/exported/AppResource.class",
            "com/jdojo/resource/exported.properties",
            "com/jdojo/encapsulated/EncapsulatedTest.class",
            "com/jdojo/encapsulated/encapsulated.properties"
        };
        System.out.println("Using a Module:");
        Module otherModule = AppResource.class.getModule();
        for (String resource : resources) {
            lookupResource(otherModule, resource);
        }
        System.out.println("\nUsing a Class:");
        Class cls = ResourceTest.class;
        for (String resource : resources) {
            // Prepend a / to all resource names to make them absolute names
            lookupResource(cls, "/" + resource);
        }
        System.out.println("\nUsing the System ClassLoader:");
        ClassLoader clSystem = ClassLoader.getSystemClassLoader();
        for (String resource : resources) {
            lookupResource(clSystem, resource);
        }
        System.out.println("\nUsing the Platform ClassLoader:");
        ClassLoader clPlatform = ClassLoader.getPlatformClassLoader();
        for (String resource : resources) {
            lookupResource(clPlatform, resource);
        }
    }
    public static void lookupResource(Module m, String resource) {
        try {
            InputStream in = m.getResourceAsStream(resource);
            print(resource, in);
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
    public static void lookupResource(Class cls, String resource) {
        InputStream in = cls.getResourceAsStream(resource);
        print(resource, in);
    }
    public static void lookupResource(ClassLoader cl, String resource) {
        InputStream in = cl.getResourceAsStream(resource);
        print(resource, in);
    }
    private static void print(String resource, InputStream in) {
        if (in != null) {
            System.out.println("Found: " + resource);
        } else {
            System.out.println("Not Found: " + resource);
        }
    }
}

下面是现实的出口:

Using a Module:
Not Found: java/lang/Object.class
Not Found: com/jdojo/resource/test/own.properties
Not Found: com/jdojo/resource/test/ResourceTest.class
Found: unnamed.properties
Found: META-INF/invalid_pkg.properties
Found: com/jdojo/opened/opened.properties
Found: com/jdojo/exported/AppResource.class
Not Found: com/jdojo/resource/exported.properties
Found: com/jdojo/encapsulated/EncapsulatedTest.class
Not Found: com/jdojo/encapsulated/encapsulated.properties
Using a Class:
Not Found: /java/lang/Object.class
Found: /com/jdojo/resource/test/own.properties
Found: /com/jdojo/resource/test/ResourceTest.class
Not Found: /unnamed.properties
Not Found: /META-INF/invalid_pkg.properties
Not Found: /com/jdojo/opened/opened.properties
Not Found: /com/jdojo/exported/AppResource.class
Not Found: /com/jdojo/resource/exported.properties
Not Found: /com/jdojo/encapsulated/EncapsulatedTest.class
Not Found: /com/jdojo/encapsulated/encapsulated.properties
Using the System ClassLoader:
Found: java/lang/Object.class
Found: com/jdojo/resource/test/own.properties
Found: com/jdojo/resource/test/ResourceTest.class
Found: unnamed.properties
Found: META-INF/invalid_pkg.properties
Found: com/jdojo/opened/opened.properties
Found: com/jdojo/exported/AppResource.class
Not Found: com/jdojo/resource/exported.properties
Found: com/jdojo/encapsulated/EncapsulatedTest.class
Not Found: com/jdojo/encapsulated/encapsulated.properties
Using the Platform ClassLoader:
Found: java/lang/Object.class
Not Found: com/jdojo/resource/test/own.properties
Not Found: com/jdojo/resource/test/ResourceTest.class
Not Found: unnamed.properties
Not Found: META-INF/invalid_pkg.properties
Not Found: com/jdojo/opened/opened.properties
Not Found: com/jdojo/exported/AppResource.class
Not Found: com/jdojo/resource/exported.properties
Not Found: com/jdojo/encapsulated/EncapsulatedTest.class
Not Found: com/jdojo/encapsulated/encapsulated.properties

lookupResource()计重载。
它们利用三个像样来恒定资源:ModuleClassClassLoader
这些艺术将资源名称与资源引用传递让print()方法来打印消息。

main()主意准备了一个资源列表,用来利用不同之资源查找方法寻找。
它保存了一个String数组列表:

// A list of resources
String[] resources = {/* List of resources */};

main()道尝试用com.jdojo.resource模块的援查找所有资源。
请注意,AppResource类在com.jdojo.resource模块中,因此AppResource.class.getModule()措施返回com.jdojo.resource模块的援。

System.out.println("Using a Module:");
Module otherModule = AppResource.class.getModule();
for (String resource : resources) {
    lookupResource(otherModule, resource);
}

该代码找到com.jdojo.resource模块中无命名、无效和开拓的包中的具有类似公事及资源。
请注意,没有找到java/lang/Object.class,因为她于java.base模块中,而未在com.jdojo.resource模块中。
同样的因由找不交com.jdojo.resource.test模块中之资源。

现在,main()计应用Resource Test类的Class靶来找到同样之资源,它以com.jojo.resource.test模块中。

Class cls = ResourceTest.class;
for (String resource : resources) {
    // Prepend a / to all resource names to make them absolute names
    lookupResource(cls, "/" + resource);
}

Class靶将只在com.jdojo.resource.test模块中固定资源,这当出口中凡判的。
在代码中,使用斜线预先填写资源名称,因为Class接近吃的资源查找方法会将资源作为不因斜线开头的相对资源名称来比,并以该类的包号添加到该资源名称。

最后,main()主意以应用程序和平台类加载器来恒定同一组资源:

ClassLoader clSystem = ClassLoader.getSystemClassLoader();
for (String resource : resources) {
    lookupResource(clSystem, resource);
}
ClassLoader clPlatform = ClassLoader.getPlatformClassLoader();
for (String resource : resources) {
    lookupResource(clPlatform, resource);
}

类似加载器将于接近加载器本身或该祖先类加载器已解的具有模块中定位资源。
系统类加载器加载com.jdojo.resource和com.jdojo.resource.test模块,因此它可以根据资源查找规则强制的限制来寻觅这些模块中的资源。
即引导类加载器从java.base模块加载Object类,因此系统类加载器可以找到java/lang/Object.class文件。

阳台类加载器不加以载com.jdojo.resource和com.jdojo.resource.test应用程序模块。
在出口中非常明显.平台类加载器只发现一个资源,java/lang/Object.class,由父类引导类加载器进行加载。

九. 走访运行时映像吃之资源

俺们来拘禁几只当运作时映像中做客资源的事例。 在JDK
9之前,可以运用ClassLoader类的getSystemResource()静态方法。
以下是于JDK 8中查找Object.class文件的代码:

import java.net.URL;
...
String resource = "java/lang/Object.class";
URL url = ClassLoader.getSystemResource(resource);
System.out.println(url);
// jar:file:/C:/java8/jre/lib/rt.jar!/java/lang/Object.class

出口显示采用jar方案返回的URL指向rt.jar文件。

JDK 9不再以JAR中蕴藏运行时映像。 它或许在将来改变成中格式存储。
JDK提供了同样栽采取jrt方案以和格式和职务无关之艺术访运行时资源的不二法门。
上面代码在JDK 9中经行使jrt方案返回一个URL,而不是jar方案:

jrt:/java.base/java/lang/Object.class

Tips
如您的代码从运行时映像看资源,并期望用jar方案的URL,则用以JDK
9中进行改动,因为在JDK 9中拿以jrt格式获取URL。

利用jrt方案的语法如下:

jrt:/<module-name>/<path>

<module-name>举凡模块的名称,<path>凡是模块中一定类还是资源文件之门径。
<module-name><path>犹是可选的。
jrt:/,指的凡保存在脚下运行时映像受之所有类和资源文件。
jrt:/<module-name>凡赖保存在<module-name>模块中之所有类和资源文件。
jrt:/<module-name>/<path>指的是<module-name>模块中名为<path>的特定类或资源文件。
以下是使jrt方案引用类文件和资源文件的星星点点只URL的示例:

jrt:/java.sql/java/sql/Driver.class
jrt:/java.desktop/sun/print/resources/duplex.png

第一个URL为java.sql模块中java.sql.Driver好像的切近公事命名。
第二个URL凡是java.desktop模块中的映像文件sun/print/resources/duplex.png命名。

Tips
好使jrt方案看运行时映像挨之资源,但是以运用ModuleClassClassLoader类吃的资源查找方法是不行看的。

好利用jrt方案创造一个URL
以下代码有显示了哪吧一个图形文件读入到Image目标被,以及当运行时映像挨拿一个类公事读入到字节数组。

// Load the duplex.png into an Image object
URL imageUrl = new URL("jrt:/java.desktop/sun/print/resources/duplex.png");
Image image = ImageIO.read(imageUrl);
// Use the image object here
System.out.println(image);
// Load the contents of the Object.class file
URL classUrl = new URL("jrt:/java.base/java/lang/Object.class");
InputStream input = classUrl.openStream();
byte[] bytes = input.readAllBytes();
System.out.println("Object.class file size: " + bytes.length);

出口结果吧:

BufferedImage@3e57cd70: type = 6 ColorModel: #pixelBits = 32 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@67b467e9 transparency = 3 has alpha = true isAlphaPre = false ByteInterleavedRaster: width = 41 height = 24 #numDataElements 4 dataOff[0] = 3
Object.class file size: 1859

哟时候可应用外花样之jrt方案,以便表示运行时映像中之有着文件以及模块中的装有文件?
可以运用jrt方案来引用一个模块来予以Java策略文件的权力。
Java策略文件中之以下条目将为java.activation模块中的代码授予所有权限:

grant codeBase "jrt:/java.activation" {
    permission java.security.AllPermission;
}

无数家伙及IDE需要枚举运行时映像中的具备模块,软件包跟文书。 JDK 9为了jrt
URL方案,附带一个只是念NIO FileSystem提供者。
可以用这个提供者列有运行时映像挨之所有类和资源文件。
有一对器与IDE将当JDK 8上运行,但将支持JDK
9的代码开发。这些家伙还需得到JDK 9运行时映像挨的接近以及资源文件列表。
当你安装JDK 9时,它在lib目录中含一个jrt-fs.jar文件。
可以用此JAR文件添加到当JDK 8上运行的家伙的近乎路径,并采用jrt
FileSystem,如下所示。

jrt文件系统包含由斜线(/)表示的根本目录,其中蕴含两单叫做吧力保跟模块的子目录:

/
/packages
/modules

以下代码有吧jrt URL方案创造了一个NIO FileSystem

// Create a jrt FileSystem
FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
The following snippet of code reads an image file and the contents of the Object.class file:
// Load an image from a module
Path imagePath = fs.getPath("modules/java.desktop", "sun/print/resources/duplex.png");
Image image = ImageIO.read(Files.newInputStream(imagePath));
// Use the image object here
System.out.println(image);
// Read the Object.class file contents
Path objectClassPath = fs.getPath("modules/java.base", "java/lang/Object.class");
byte[] bytes = Files.readAllBytes(objectClassPath);
System.out.println("Object.class file size: " + bytes.length);

输出结果吗:

BufferedImage@5f3a4b84: type = 6 ColorModel: #pixelBits = 32 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@5204062d transparency = 3 has alpha = true isAlphaPre = false ByteInterleavedRaster: width = 41 height = 24 #numDataElements 4 dataOff[0] = 3
Object.class file size: 1859

以下代码有将打印运行时映像受有着模块中之所有类和资源文件。
类似地,可以吧确保创建·Path`好像列举运行时映像挨的所有包。

// List all modules in the runtime image
Path modules = fs.getPath("modules");
Files.walk(modules)
     .forEach(System.out::println);

出口结果也:

/modules
/modules/java.base
/modules/java.base/java
/modules/java.base/java/lang
/modules/java.base/java/lang/Object.class
/modules/java.base/java/lang/AbstractMethodError.class
...

咱来拘禁一个自运行时映像看资源的整体程序。
下面包含名为吧com.jdojo.resource.jrt的模块的模块声明。

// module-info.java
module com.jdojo.resource.jrt {
    requires java.desktop;
}

属下是JrtFileSystem看似的源代码,它位于com.jdojo.resource.jrt模块中。

// JrtFileSystem.java
package com.jdojo.resource.jrt;
import java.awt.Image;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import javax.imageio.ImageIO;
public class JrtFileSystem {
    public static void main(String[] args) throws IOException {
        // Create a jrt FileSystem
        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
        // Load an image from a module
        Path imagePath = fs.getPath("modules/java.desktop", "sun/print/resources/duplex.png");
        Image image = ImageIO.read(Files.newInputStream(imagePath));
        // Use the image object here
        System.out.println(image);
        // Read the Object.class file contents
        Path objectClassPath = fs.getPath("modules/java.base", "java/lang/Object.class");
        byte[] bytes = Files.readAllBytes(objectClassPath);
        System.out.println("Object.class file size: " + bytes.length);
        // List 5 packages in the runtime image
        Path packages = fs.getPath("packages");
        Files.walk(packages)
             .limit(5)
             .forEach(System.out::println);
        // List 5 modules’ entries in the runtime image
        Path modules = fs.getPath("modules");
        Files.walk(modules)
             .limit(5)
             .forEach(System.out::println);
    }
}

输出结果吗:

BufferedImage@5bfbf16f: type = 6 ColorModel: #pixelBits = 32 numComponents = 4 color space = java.awt.color.ICC_ColorSpace@27d415d9 transparency = 3 has alpha = true isAlphaPre = false ByteInterleavedRaster: width = 41 height = 24 #numDataElements 4 dataOff[0] = 3
Object.class file size: 1859
packages
packages/com
packages/com/java.activation
packages/com/java.base
packages/com/java.corba
modules
modules/java.desktop
modules/java.desktop/sun
modules/java.desktop/sun/print
modules/java.desktop/sun/print/resources

JrtFileSystem类似,演示使用jrt URL方案由运行时映像看资源。
小心,程序仅打包和模块目录中的五独章。
可以看java.desktop模块中之sun/print/resources/duplex.png。
java.desktop模块不打开sun.print.resources包。
使用ModuleClassClassLoader类吃的旁资源查找方法来稳定
sun/print/resources/duplex.png将砸。

十. 使用JDK内部API

JDK由公共API和其中API组成。 公共API旨在用于支付可移植Java应用程序。
JDK中之java.*javax.*org.*保险包含在公共API。
如果应用程序仅以公共API,则可以支撑Java平台的兼具操作系统及运行。
这种用提供的任何一个保证是,如果其当JDK版本N中工作,它用连续以JDK本N +
1中劳作。

com.sun.*sun.*jdk.*包用于贯彻JDK本身,它们组成内部API,这不意味着由开发人员使用。
内部API不能够保证在有操作系统及运行。
com.sun.*sun.*顶软件包是Oracle JDK的如出一辙部分。
如果用其它供应商之JDK,这些软件包用无可用。 非Oracle
JDK(如IBM的JDK)将运用其他软件包号来促成其内部API。
下图展示了不同门类的JDK API。

图片 7

每当JDK 9模块化之前,可以利用其他JAR的公共类,即使这些近似是JDK内部API。
开发人员和片广泛运用的仓库已经采取JDK内部API来便宜,或者出于这些API提供的功能难以在JDK之外实现。
这些类似的示范是BASE64EncoderBASE64Decoder
开发人员为了方便使用它们,它们可看做sun.misc包中之JDK内部API使用,即使它不难开发。
另一个常见应用的近乎是sun.misc包中的Unsafe接近。
在JDK之外开发一个近似来取代Unsafe类似,因为她访问了JDK内部是好拮据的。

单纯用于方便使用的中API在JDK之外不受使用,或者它们所是的支撑的轮换已经被分类也未要中API,并且都查封装在JDK
9中。示例是Sun.misc包中之BASE64EncoderBASE64Decoder类,JDK
8里,Base64.EncoderBase64.Decoder`看似作为公共API的一致片添加到java.util包被。

在JDK之外广泛利用可难以支付之里API被分门别类为关键的内API。
如果是替换,它们于查封装在JDK 9中。 封装于JDK
9中只是足以利用命令行选项的主要中API已使@jdk.Exported注解。 JDK
9不提供以下类的代,这些近似让认为是重要之其中API。
它们得以经过jdk.unsupported模块访问。

com.sun.nio.file.ExtendedCopyOption
com.sun.nio.file.ExtendedOpenOption
com.sun.nio.file.ExtendedWatchEventModifier
com.sun.nio.file.SensitivityWatchEventModifier
sun.misc.Signal
sun.misc.SignalHandler
sun.misc.Unsafe
sun.reflect.Reflection
sun.reflect.ReflectionFactory

Tips
每当JDK
9中,大多数JDK内部API已封闭装于模块中,默认情况下不得看。但依然可应用--add-read切莫标准命令行选项访问它们。

以下类中之addPropertyChangeListener()removePropertyChangeListener()艺术就在JDK
8中弃用,并曾自JDK 9中删除:

java.util.logging.LogManager
java.util.jar.Pack200.Packer
java.util.jar.Pack200.Unpacker

好用在JAVA_HOME\bin目录中的jdeps工具来寻找代码在JDK内部API上的类级依赖关系。
还用运用--jdk-internals分选,如下所示:

jdeps --jdk-internals --class-path <class-path> <input-path>

这里,<input-path>足是相仿公事,目录或JAR文件的门径。
该令分析<input-path><class-path>齐的所有类。
以下命令打印jersey-common.jar文件中JDK内部API的用法,假设JAR位于C:\Java9Revealed\extlib目录中。

C:\Java9Revealed>jdeps --jdk-internals extlib\jersey-common.jar

下是有的输出:

jersey-common.jar -> jdk.unsupported
   org.glassfish.jersey.internal.util.collection.ConcurrentHashMapV8 -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
org.glassfish.jersey.internal.util.collection.ConcurrentHashMapV8$TreeBin -> sun.misc.Unsafe                                    JDK internal API (jdk.unsupported)
...

十一. 修修补补模块内容

有时候,可能要因此外一个本替换特定模块的好像公事以及资源拓展测试和调剂。
在JDK 9之前,可以应用-Xbootclasspath/p选取来贯彻之目的。 此选项都以JDK
9中去。在JDK 9中,需要用--patch-module不标准命令行选项。
此选项可用于javac与java命令。 其语法如下:

--patch-module <module-name>=<path-list>

这里,<module-name>举凡正值替换其内容之模块的称呼。
<path-list>是带有新模块内容之JAR或目录列表;
列表中的每个元素还是因为特定于主机的途径分隔符分隔,该字符是Windows上之分公司以及类UNIX平台上之冒号。

可针对同一命令多次施用--patch-module挑选,因此可以补多只模块的情节。
可以补应用程序模块,库模块和平台模块。

Tips
当使用--patch-module选时,无法交替module-info.class文件。
试图这样做是默认无视的。

而今,我们将运行一个补com.jdojo.intro模块的例子。
使用新的Welcome.class文本替换此模块中的Welcome.class文本。
回想一下,我们以第3节中开创了Welcome接近。新类将打印一个两样之音。
新的好像声明如下所示。 在源代码中,此类位于com.jdojo.intro.patch
的NetBeans项目被。

// Welcome.java
package com.jdojo.intro;
public class Welcome {
    public static void main(String[] args) {
        System.out.println("Hello Module System.");
        // Print the module name of the Welcome class
        Class<Welcome> cls = Welcome.class;
        Module mod = cls.getModule();
        String moduleName = mod.getName();
        System.out.format("Module Name: %s%n", moduleName);
    }
}

当今,需要采取以下命令为者新的Welcome类编译源代码:

C:\Java9Revealed>javac -Xmodule:com.jdojo.intro
  --module-path com.jdojo.intro\dist
  -d patches\com.jdojo.intro.patch com.jdojo.intro.patch\src\com\jdojo\intro\Welcome.java

不怕去除前片只选项:-Xmodule-module-path,此命令也将不负众望。
但是,当编译平台类(如java.util.Arrays)时,将待这些选择。
否则,将接错误。-Xmodule分选指定要编译的源代码所属的模块名称。
--module-path选料指定在哪寻找-Xmodule选择中指定的模块。
这些选择用于固定编译新类所急需的别样类似。
在这种气象下,Welcome仿佛不靠让com.jdojo.intro模块中之另其它类似。
这便是为何以这种状态下删除这些选择不见面影响结果。-d选择指定编译的Welcome.class文件的保存位置。

以下是自com.jdojo.intro模块运行原始Welcome类似的命令:

C:\Java9Revealed>java --module-path com.jdojo.intro\dist
--module com.jdojo.intro/com.jdojo.intro.Welcome

出口结果为:

Welcome to the Module System.
Module Name: com.jdojo.intro

而今凡是运用修补版本运行Welcome看似的下了。 这是实行之操作的吩咐:

C:\Java9Revealed>java --module-path com.jdojo.intro\dist
  --patch-module com.jdojo.intro=patches\com.jdojo.intro.patch
  --module com.jdojo.intro/com.jdojo.intro.Welcome

输出结果也:

Hello Module System.
Module Name: com.jdojo.intro

当使用--patch-module挑时,在探寻模块路径之前,模块系统会寻找是选项中指定的路子。
请注意,此选项中指定的不二法门包含模块的情,但这些途径不是模块路径。

十二. 总结

而用旧版应用程序迁移至JDK 9,JDK
9进行了一些突破性的更动,这点必小心。

JDK
9中针对JDK的非直观版本控制方案都开展了改进。JDK版本字符串由以下四单要素做:版本号,预发布信息,构建信息及附加信。
只生第一个是强制性的。
正则表达式$vnum(-$pre)?(\+($build)?(-$opt)?)?概念了本字符串的格式。
一个简单版本的字符串只含前片独元素:一个本号,可卜的是预发布信息。
可以有一个简便到“9”的本子字符串,其中单独包含主版本号。“99.0.1-ea+154-20170130.07.36am”,这个版字符串包含了富有因素。

JDK
9添加了一个称吧Runtime.Version的静态嵌套类,其实例表示JDK版本字符串。
该类没有国有构造函数。
获取其实例的唯一方式是调整用那静态方法名parse(String vstr)
如果版本字符串为空或无效,该方法恐怕会见废弃来运行时大。
该类包含几只措施来取版本的不同部分。

JDK 9更改了JDK和JRE安装的目布局。
现在,除了JDK安装包含开发工具和JRE不分包的JMOD格式的平台模块的正片之外,JDK和JRE安装之间无分。
可以构建协调之JRE(使用jlink工具),它可涵盖JRE中待之JDK的任何部分。

以Java SE
9之前,可以应用“支持的正规化覆盖机制”来采取实现“承认标准”或“独立API”的比较新本子的好像与接口。
这些包括在Java Community Process之外创建的javax.rmi.CORBA包和Java API
for XML Processing(JAXP)。 Java SE 9仍然支撑这种体制。 在Java SE
9中,需要用--upgrade-module-path命行选项。
此选项之值是含有标准专业和独立API的模块的目录列表。

当版本9之前的Java
SE允许一个恢弘机制,可以透过以JAR放在系统性能java.ext.dirs指定的目录中来扩大运行时映像。
如果未设置是系统特性,则利用jre\lib\ext目录作为其默认值。 Java SE
9不支持扩大机制。 如果需要接近的职能,可以以这些JAR放在类路径的先头。

以版本9之前,JDK以三单近乎加载器来加以载类。
他们是引导类加载器,扩展类加载器和体系(应用程序)类加载器。
它们分层排列 ——
没有父类的导类加载器,引导类加载器作为扩大类加载器的父类,并扩大类加载器作为系统类加载器作为之父级。
在尝加载类型我之前,类加载器将项目加载要求委托给那父类(如果出)。
JDK 9保持了三像样装载机的通向后兼容性。 JDK
9不支持扩大机制,所以扩展类加载器没有意思。 JDK
9已经拿扩充类加载器重命名也平台类加载器,该引用得使用ClassLoader看似的静态方法getPlatformClassLoader()获得。
在JDK 9中,每个接近加载器加载不同品类的模块。

当JDK
9中,默认情况下封装命名模块中之资源。只有当资源处于不命名,无效或打开的包中时,命名模块中的资源才能够吃别一个模块中之代码访问。名称以.class(所有类似公事)结尾的命名模块中之备资源都好经过其它模块中之代码访问。可以动用jrt方案的URL来拜访运行时映像受的其他资源。

当JDK 9之前,可以采取JDK内部API。 JDK
9中之大部JDK内部API已被装进。有些通过jdk.unsupported模块来提供。可以利用jdeps工具及--jdk-internals分选来查找代码对JDK内部API的类级依赖性。

偶尔,可能得用别样一个版替换特定模块的接近公事和资源进行测试与调剂。在JDK
9之前,可以下都当JDK 9中删去的-Xbootclasspath/p摘来兑现。在JDK
9中,需要利用--patch-module勿标准命令行选项。
javac和java命令可以运用这个选项。

相关文章