日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java 9 揭秘

發布時間:2024/1/23 java 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java 9 揭秘 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

做一個終身學習的人。

Java 9

在本章,主要介紹以下內容:

  • 新的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.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 elementString 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 endpublic 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目錄下的目錄。

Java SE 9之前的JDK和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目錄布局

在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加載類中的三個類加載器。

版本9之前的JDK中的類加載器層次結構

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類加載器層次結構如下圖所示。

JDK 9中的加載器層次結構

請注意,在JDK 9中,應用程序類加載器可以委托給平臺類加載器以及引導類加載器;平臺類加載器可以委托給引導類加載器和應用程序類加載器。 以下詳細介紹JDK 9類加載器的工作原理。

在JDK 9中,引導類加載器是由類庫和代碼在虛擬機中實現的。 為了向后兼容,它在程序中仍然由null表示。 例如,Object.class.getClassLoader()仍然返回null。 但是,并不是所有的Java SE平臺和JDK模塊都由引導類加載器加載。 舉幾個例子,引導類加載器加載的模塊是java.base,java.logging,java.prefs和java.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類中所有方法的資源名稱都是絕對的,它們不以斜線(/)開頭。 例如,當調用ClassLoader的getSystemResource()方法時,將使用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.classfindClassResource("java/lang/Object.class");// An absolute resource name - Will find Object.classfindClassResource("/java/lang/Object.class");// A relative resource name - will find the classfindClassResource("ResourceTest.class");// Load the wordtonumber.properties fileloadProperties("/wordtonumber.properties");// Will not find the properties because we are using// an absolute resource nameloadProperties("/resources/numbertoword.properties");// Will find the propertiesloadProperties("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

Class和ClassLoader類沒新增任何新的方法。 Module類包含一個getResourceAsStream(String name)方法,如果找到該資源,返回一個InputStream;否則返回null。



作者:M104
鏈接:https://www.jianshu.com/p/b133abd54d27
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權并注明出處。

總結

以上是生活随笔為你收集整理的Java 9 揭秘的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。