从JDK8迁移到更高版本的JDK【Java8迁移到Java11指南】【Java8升级到Java11指南】

从JDK8迁移到更高版本的JDK

开始

本指南的目的是帮助您识别潜在的问题,并就如何将现有的Java应用程序迁移到最新的JDK版本提供建议。该指南还重点介绍了最新版本所做的重大更改和改进。

项目重点排查:

过时的api

第三方库

有使用到正则表达^符号的地方,java8和java9以上,^符号语义不同

java8 [^]不包含嵌套,Java9以上,[^]作用于嵌套

java8,[^a-d&&e-f] 先执行[^a-d] 即先执行^运算符,先取反在取交集,Java9以上就先执行&&,即先执行[a-d&&e-f]取交集,然后再取反

jvm选项和GC选项

对GC日志输出的更改**-XX:+PrintGCDetails和-XX:+PrintGC选项已被弃用。**

本指南包含以下部分:

JDK 11发布中的重要变化

在将应用程序迁移到JDK 11之前,您应该了解它与JDK 10版本之间的更新和更改。如果您正在从JDK 8开始迁移,那么您还应该熟悉JDK 8与更新版本之间的差异,这些差异在从JDK 8迁移到更高版本的JDK中有所描述。

以下是JDK 11中的一些重要变化:

  • JDK 不再包含 JRE 和 Server JRE ;因此自动更新不再可用。

  • Oracle不再提供32位Windows下载。

  • JDK中不支持Java Web Start【javaws.exe】、Java Plugin和Java Control Panel等tool工具。请参阅移除部署堆栈Removal of the Deployment Stack

  • JavaFX不再包含在JDK中。现在可以从https://openjfx.io/单独下载。

    JavaFX(JavaFX Script)是一个用于构建桌面、Web和移动应用程序的高性能用户界面(UI)工具包,全称为JavaFX Script。它提供了丰富的图形用户界面组件,如窗口、按钮、表格、图表等,并支持CSS样式、动画、多媒体和Web服务集成。JavaFX最初是由Sun Microsystems开发的,后来由Oracle接手并在Java 8中作为Java SDK的标准部分提供。JavaFX提供了跨平台的GUI开发能力,使得开发者可以用一套代码构建能在多种设备和平台上运行的应用程序。

    然而,从Java 11开始,Oracle JDK取消了JavaFX的捆绑,将其作为一个单独的模块进行分发和维护。这意味着从Java 11起,如果要在Java项目中使用JavaFX,开发者需要单独下载并添加JavaFX库到项目的类路径中。

    目前,JavaFX由开源社区(OpenJFX)维护,可以通过Maven Central等仓库获取相关依赖,并与OpenJDK配合使用。JavaFX现在已被广泛应用于桌面应用、媒体应用、数据分析应用等多种领域,尤其适合需要丰富视觉效果和交互体验的应用场景。

  • JAXB和JAX-WS不再与JDK捆绑在一起。参见Java EE和CORBA模块的移除。

此外,还有与安全相关的更新,以及需要注意的一些已删除的工具和组件 参阅:

删除部署堆栈

Java部署技术在JDK 9中已弃用,并在JDK 11中删除。

Java插件、 Java Applet Viewer、Java控制面板和Java Web Start【即javaws.exe】工具,已在JDK 11中删除。

请参阅删除Java部署技术Remove Java Deployment Technologies

删除Java EE和CORBA模块

在JDK 11中,删除了Java EE和CORBA模块。这些模块在JDK 9中已被弃用。

JavaEE(Java Platform, Enterprise Edition)的全称以前是Java 2 Platform, Enterprise Edition (J2EE),

是为企业级应用软件开发设计的一套标准规范和技术平台。

JavaEE并不是Java Development Kit (JDK)的一部分,而是建立在JDK之上的一个标准框架,

它定义了一整套用于开发和部署可扩展、安全、可靠且多层的企业级Java应用程序的服务、接口和API。

JavaEE提供了一系列服务和组件模型,包括但不限于:

•Servlets 和 JSP(用于Web应用开发)

•EJB(Enterprise JavaBeans,企业级组件模型)

•JMS(Java Message Service,消息服务)

•JDBC(Java Database Connectivity,数据库连接)

•JTA(Java Transaction API,事务处理)

•JPA(Java Persistence API,持久化)

•JAAS(Java Authentication and Authorization Service,认证和授权服务)

JavaEE的作用在于简化和标准化复杂的企业级应用开发过程,促进各组件之间的互操作性,并支持分布式计算环境下的各种服务。

实际上,JavaEE并未“被JDK移除”,因为它们本来就是两个不同的事物。不过,在Java发展过程中,JavaEE的管理和开发模式发生了变化。

2017年,Oracle决定将JavaEE的管理权转移给Eclipse基金会,并随后将其重命名为Jakarta EE。

这一转变旨在通过开源社区驱动的方式加速Java企业级技术的创新和发展。

在Maven项目中,你需要在pom.xml文件中添加对应的Jakarta EE依赖,而不是老的javax.*开头的依赖。、对于部分API,由于Java EE到Jakarta EE的过渡,包名也有所变化,如 javax.servlet.*改为jakarta.servlet.*,因此在代码中引用API时也需要相应地更新包名。

被删除的模块有:

  • Java.xml.ws: Java API for XML Web Services (JAX-WS),用于Java平台的Web服务元数据,以及Java的SOAP附件(SAAJ)

    JAX-WS(Java API for XML Web Services)是Java平台用于创建和访问Web服务的官方规范,全称为Java API for XML-Based Web Services。JAX-WS提供了一套标准API和工具,允许开发者使用Java语言轻松地开发、部署和调用基于SOAP协议的Web服务。

    开发者可以通过Maven等构建工具添加相关依赖来继续开发和使用JAX-WS服务和客户端。

    从Java 9之后,Web服务相关的技术逐步倾向于采用RESTful风格的JAX-RS(Java API for RESTful Web Services)。

    SOAP(Simple Object Access Protocol)的全称是“简单对象访问协议”。它是一种基于XML(eXtensible Markup Language)的消息传递协议,用于在网络尤其是Web上实现分布式系统的互操作性,特别是在Web服务应用中。

    SAAJ(SOAP with Attachments API for Java)的全称是“Java中带有附件的SOAP API”。

    作用: SAAJ是Java平台提供的一组API,用于创建、发送和接收包含附件的SOAP消息。在SOAP协议的基础上,SAAJ允许在XML消息体之外附加二进制数据,如图像、音频、视频、文档等非XML格式的内容。SAAJ通过实现SOAP协议中的MTOM(Message Transmission Optimization Mechanism)标准,提供了高效传输这些附件的方法。

    需要注意的是,随着Web服务架构的发展,RESTful风格的API逐渐成为主流,而SOAP和SAAJ的使用相对减少

  • Java.xml.bind: Java Architecture for XML Binding (JAXB) 用于XML绑定的Java架构(JAXB)

  • java.xml.ws.annotation: Java SE为支持web services而定义的JSR-250通用注解的子集

  • java.corba: corba

  • CORBA(Common Object Request Broker Architecture)的全称是公共对象请求代理结构。它是由OMG(Object Management Group)组织制定的一种分布对象计算的标准,允许不同语言、平台和操作系统之间的对象互相调用,实现跨平台的分布式计算。

    随着技术的发展,尤其是Java EE和Web服务等技术的兴起,CORBA的使用逐渐减少。从Java 11开始,Oracle官方去除了Java SE中的CORBA模块

  • java.transaction: 由Java SE定义的Java事务API的子集,用于支持CORBA对象

  • java.activation: JavaBeans激活框架

  • java.se.ee:上述六个模块的聚合模块

  • jdk.xml.ws: JAX-WS的工具

  • jdk.xml.bind: JAXB的工具

如果不对构建进行修改,那么引用这些API中类的现有代码将无法编译。同样,如果不对应用程序的部署方式进行更改,那么类路径上引用这些API中类的代码将会出现NoDefClassFoundError或ClassNotFoundException错误。

参见JEP 320:移除Java EE和CORBA模块,以获取有关这些模块可能替代品的更多信息。

注意: 您可以从Maven下载JAXB和JAX-WS。

安全更新

JDK 11发布中的安全更新

JDK 11版本包括传输层安全(TLS) 1.3规范(RFC 8446)的实现。

传输层安全协议(TLS)1.3是最新版本(2018年8月),并在JDK 11中默认启用。该版本不仅关注速度提升,而且通过强调现代加密实践来更新协议的整体安全性,并禁止使用过时或弱加密算法。(例如,不再允许RSA密钥交换和纯DSA签名。)

TLS 1.3协议中添加了几个特性来改进向后兼容性,但是有几个问题需要注意。具体请参见JEP 332。

移除安全证书

在JDK 11中,已从密钥库中删除了以下根证书:

使用已删除证书的产品可能不再起作用。如果需要这些证书,则必须配置并填充cacerts以填充缺失的证书。要将证书添加到信任库中,请参阅Java Platform,Standard Edition Tools Reference指南中的keytool。

移除api、工具和组件

介绍JDK 11中删除的api、工具和组件的详细信息。

删除了JDK 11中的api

JDK 11中删除了以下api。这些api中的许多在以前的版本中已弃用,并已被较新的api所取代。有关可能的替代方案的信息,请参见JDK 11 API Specification

javax.security.auth.Policy 
java.lang.Runtime.runFinalizersOnExit(boolean)
java.lang.SecurityManager.checkAwtEventQueueAccess() 
java.lang.SecurityManager.checkMemberAccess(java.lang.Class,int)
java.lang.SecurityManager.checkSystemClipboardAccess()
java.lang.SecurityManager.checkTopLevelWindow(java.lang.Object)
java.lang.System.runFinalizersOnExit(boolean)
java.lang.Thread.destroy()
java.lang.Thread.stop(java.lang.Throwable)

不随JDK 11一起提供的工具和组件

以下是不随JDK 11一起提供的工具和组件的列表。

主要工具

  • appletviewer

参见JDK-8200146:删除appltviewer启动器。

CORBA工具

  • idlj
  • orbd
  • servertool
  • tnamesrv

此外,RMI编译器将不再支持-idl或-iiop选项。请参见JDK 11发行说明。

Java Web Services服务工具

  • schemagen
  • wsgen
  • wsimport
  • xjc

参见JEP 320:移除Java EE和CORBA模块。

Java部署工具

  • javapackager
  • javaw

注意:

pack200和unpack200已弃用,可能会在未来的JDK版本中删除。

参见从JDK和JEP 336中移除JavaFX:弃用Pack200工具和API。

监控工具

  • jmc:【java mission control :java任务控制】在JDK 11中,JMC作为独立的软件包提供,不再捆绑在JDK中。

请参阅 Removal of JMC from JDK and Java Mission Control

JVM-MANAGEMENT-MIB.mib

通过SNMP JVM-MANAGEMENT-MIB.mib进行JVM监控和管理的规范已被删除。

参见 Removal of JVM-MANAGEMENT-MIB.mib.

SNMP代理

jdk.snmp模块被移除。请参见Removal of SNMP Agent.。

Oracle Desktop 特定删除项

为迁移做准备

以下部分将帮助您成功迁移应用程序:

下载最新的JDK

下载并安装最新的JDK版本【这里指java11】。

在重新编译之前先运行程序

尝试在最新的JDK版本(JDK 11)上运行应用程序。大多数代码和库都可以在JDK 11上运行,不需要任何更改,但可能有一些库需要升级。

注意:

迁移是一个迭代过程。你或许会发现,首先运行你的程序(这个任务),然后或多或少并行完成这三个任务,这样效果可能最好:

在运行应用程序时,查找JVM关于过时VM选项的警告。如果虚拟机启动失败,则查找已删除的GC选项,参阅Removed GC Options

如果应用程序成功启动,请仔细检查测试并确保其行为与使用的JDK版本相同。

例如,一些早期采用者注意到他们的日期和货币格式不同。请参阅默认使用CLDR区域设置数据。

要使您的代码在最新的JDK版本上运行,请了解每个JDK版本中的新特性和更改。

即使您的程序看起来成功运行,您也应该完成本指南中的其余步骤,并查看问题列表。

更新第三方库

对于您使用的每个工具和第三方库,您可能需要一个支持最新JDK版本的更新版本。

检查您的第三方库和工具供应商的网站,看是否有设计用于最新JDK的库或工具版本。如果存在,请下载并安装新版本。

如果您使用Maven或Gradle构建应用程序,则请确保升级到支持最新JDK版本的较新版本

如果您使用IDE开发应用程序,那么它可能有助于迁移现有代码。NetBeans、Eclipse和IntelliJ IDE都提供了支持最新JDK的版本

您可以在OpenJDK wiki的 Quality Outreach上查看许多Free Open Source Software(FOSS)项目使用OpenJDK构建的测试状态。

如果需要,编译你的应用程序

使用最新的JDK编译器编译代码将简化迁移到未来版本的过程,因为代码可能依赖于api和特性,而这些api和特性已被确定为有问题。然而这并不是绝对必要的。

如果你需要用JDK 11编译器编译你的代码,那么请注意以下几点:

  • 如果您在源代码中使用下划线字符(“_”)作为单字符标识符,那么您的代码将无法在JDK 11中编译。它在JDK 8中生成一个警告,并从JDK 9开始生成一个错误。

    举个例子:

    static Object _ = new Object();
    

    这段代码从编译器生成以下错误消息:

    java:2: error:从版本9开始,'_'是一个关键字,不能用作合法的标识符。
    
  • 如果您在javac中使用-source和-target选项,那么请检查所使用的值。

    支持的-source/-target值为11(默认值)、10、9、8、7和6(6已弃用,使用该值时将显示警告)。

    在JDK 8中,1.5/5及更早的-source和-target值已被弃用,并会导致警告。在JDK 9及以上版本中,这些值会导致错误。

    >javac -source 5 -target 5
    错误:源选项5不再被支持。使用6或更高版本。
    错误:不再支持目标选项1.5,请使用1.6或更高版本。
    

    如果可能的话,使用新的–release标志而不是-source和-target选项。参见Java平台标准版工具参考中的 javac

    –release标志的有效参数遵循与-source和-target相同的策略,即一个加三个回退。

    javac可以识别和处理所有以前JDK的类文件,一直追溯到JDK 1.0.2的类文件。

    参见JEP 182:退出javac -source和-target选项的策略。 JEP 182: Policy for Retiring javac -source and -target Options

  • 在JDK 11中,仍然可以访问sun.misc.Unsafe等关键内部JDK API,但大多数JDK的内部API在编译时无法访问。

    您可能会遇到编译错误,表明您的应用程序或其库依赖于内部API。

    为了确定依赖项,请运行Java依赖关系分析工具。请参阅 Run jdeps on Your Code。如果可能,请更新您的代码以使用受支持的替代API。

    您可以使用–add-exports选项作为临时解决方案,以编译包含对JDK内部类引用的源代码。

    您可能会看到比以前更多的弃用警告。

在代码上运行jdeps

在应用程序上运行jdeps工具,查看应用程序和库所依赖的包和类。如果您使用内部api,那么jdeps可能会建议替换以帮助您更新代码。

要查找对内部JDK api的依赖,请使用【- jdkinternals】选项运行jdeps。例如,如果你在一个调用sun.misc.BASE64Encoder的类上运行jdeps,你会看到:

>jdeps -jdkinternals Sample.class
Sample.class -> JDK removed internal API
   Sample  -> sun.misc.BASE64Encoder  JDK internal API (JDK removed internal API)

Warning: JDK internal APIs are unsupported and private to JDK implementation that are
subject to be removed or changed incompatibly and could break your application.
Please modify your code to eliminate dependency on any JDK internal APIs.
For the most recent update on JDK internal API replacements, please check:
https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool

JDK Internal API                         Suggested Replacement
----------------                         ---------------------
sun.misc.BASE64Encoder                   Use java.util.Base64 @since 1.8

如果您使用Maven,有一个可用的jdeps插件。

有关jdeps的语法,请参见Java平台标准版工具参考中的jdeps

请记住,jdeps是一种静态分析工具,代码的静态分析可能不会提供依赖项的完整列表。如果代码使用反射来调用内部API,那么jdeps不会警告您。

从JDK 8迁移到更高版本的JDK

JDK 8和后来的JDK版本之间有重大的变化。

每个新的Java SE版本都会引入一些与以前版本不兼容的二进制文件、源代码和行为。JDK 9中Java SE平台的模块化带来了许多好处,但也带来了许多变化。仅使用官方Java SE平台api和受支持的特定于jdk的api的代码应该可以继续工作而无需更改。使用jdk内部api的代码应该继续运行,但应该迁移到使用受支持的api。

以下部分描述了在将JDK 8应用程序迁移到以后的JDK版本时应该注意的JDK包和api中的更改。

查看在运行应用程序时可能遇到的更改列表。

当您的应用程序在最新版本的JDK上成功运行时,请查看“下一步步骤”,这将帮助您避免在未来版本中出现问题。

理解运行时访问警告

一些工具和库使用反射来访问JDK中仅供内部使用的部分。这种非法的反射访问将在JDK的未来版本中被禁用。目前,默认情况下允许这样做,并发出警告。

例如,下面是启动Jython时发出的警告:

>java -jar jython-standalone-2.7.0.jar
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by jnr.posix.JavaLibCHelper (file:/C:/Jython/jython2.7.0/jython-standalone-2.7.0.jar) to method sun.nio.ch.SelChImpl.getFD()
WARNING: Please consider reporting this to the maintainers of jnr.posix.JavaLibCHelper
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Jython 2.7.0 (default:9987c746f838, Apr 29 2015, 02:25:11)

如果看到这样的警告,请联系工具或库的维护人员。警告的第二行指定了JAR文件的名称,该文件的代码使用反射来访问JDK的内部部分。

默认情况下,在java启动器启动的进程的生命周期中,最多发出一次关于反射访问的警告

发出警告的确切时间取决于执行反射访问操作的工具和库的行为。该警告可能在进程生命周期的早期出现,也可能在启动后很长一段时间出现。

可以通过使用–add-opens命令行标志,逐个库禁用警告消息。例如,您可以通过以下方式启动Jython:

>java --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED -jar jython-standalone-2.7.0.jar
Jython 2.7.0 (default:9987c746f838, Apr 29 2015, 02:25:11)

这一次,没有发出警告,因为java调用显式地确认了反射访问。正如您所看到的,您可能需要指定多个–add-opens标志来覆盖库在类路径上尝试的所有反射访问操作。

为了更好地理解工具和库的行为,可以使用==–illegal-access**=warn**==命令行标志。此标志会对每个非法的反射访问操作发出警告消息。

此外,通过设置–illegal-access**=debug**,您可以获得有关非法反射访问操作的详细信息,包括堆栈跟踪。

如果您已经更新了库,或者当您获得它们时,那么您可以尝试使用 --illegal-access=deny命令行标志。它禁用所有的反射访问操作,除了那些由其他命令行选项启用的操作,比如--add-opens。这将是未来版本的默认模式。

有两个选项,允许您将以特定方式封装。你可以把它们和 --illegal-access=deny,或者,如前所述,压制警告。

  • 如果需要使用已无法访问的内部API,则使用--add-exports运行时选项。您也可以在编译时使用--add-exports来访问内部API

    <!--在Java 11及更高版本中,模块系统引入了严格的封装原则,一些内部API(默认未导出的模块中的包或类)在默认情况下是不可见的。如果需要在模块A中访问模块B中未导出的API,可以使用--add-exports命令行选项在编译时临时开放访问权限。以下是使用javac命令编译时添加出口的示例:-->
    javac --add-exports moduleB/pkg=target-moduleA \
          -cp your-classpath \
          YourJavaFile.java
    <!--在这个命令中:•moduleB是包含你想访问的内部API的模块名。•pkg是你希望导出的包的名称。•target-moduleA是你想要允许访问这些API的模块名。-->
    
    <!-- 如果你正在使用maven-compiler-plugin之类的Maven插件编译项目,可以在插件配置中加入类似的参数: -->
    <project>
      [...]
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
              <release>11</release>
              <compilerArgs>
                <!-- 添加模块间的导出 -->
                <arg>--add-exports</arg>
                <arg>moduleB/pkg=target-moduleA</arg>
              </compilerArgs>
            </configuration>
          </plugin>
        </plugins>
      </build>
      [...]
    </project>
    请注意,使用--add-exports绕过模块系统的封装性是一种临时解决方案,不推荐在生产环境中长期依赖这种方式访问内部API,因为这些API随时都可能发生变化,且不受稳定的API承诺支持。在可能的情况下,应优先寻找稳定的公共API来满足需求。
    
  • 如果必须允许类路径上的代码执行深度反射以访问非公共成员,则使用--add-opens选项。

如果您想抑制所有反射访问警告,那么在需要的地方使用–add-exports和–add-opens选项。

–add-exports添加导出

如果你必须使用默认设置为不可访问的内部API,那么你可以使用 --add-exports 命令行选项来打破封装。

–add-exports 选项的语法是:

--add-exports <source-module>/<package>=<target-module>(,<target-module>)*

<source-module>< target-module >是模块名称, <package>是包的名称。

如果目标模块读取源模块,那么–add-exports选项允许目标模块中的代码访问源模块的命名包中的类型。

作为特例,如果 < target-module >ALL-UNNAMED,那么源包会被导出到所有未命名的模块,无论它们最初存在的还是后来创建的。例如:

--add-exports java.management/sun.management=ALL-UNNAMED

这个示例允许所有未命名模块(类路径上的代码)中的代码访问 java.management/sun.management 中公共类型的公共成员。如果类路径上的代码尝试进行深度反射以访问非公共成员,则代码将失败。

如果一个在类路径上运行名称为oldApp的应用程序 , 如果必须使用 java.management 模块中未导出的 com.sun.jmx.remote.internal 包,那么它所需的访问权限可以通过以下方式授予:

--add-exports java.management/com.sun.jmx.remote.internal=ALL-UNNAMED

您还可以使用JAR文件清单来破坏封装:

Add-Exports:java.management/sun.management

谨慎使用 --add-exports 选项。您可以使用它来访问库模块的内部 API,甚至访问 JDK 本身,但您必须自己承担风险。

如果那个内部 API 发生更改或被删除,那么您的库或应用程序就会失败。

另见JEP 261

–add-opens

如果你必须允许类路径上的代码进行深度反射以访问非公开成员,那么请使用 –add-opens 运行时选项。

有些库会进行深度反射,即设置 setAccessible(true),以便可以访问所有成员,包括私有成员。你可以通过在 java 命令行上使用 --add-opens 选项来授予这种访问权限。使用这个选项不会产生警告消息。

如果设置了 --illegal-access=deny,并且你在运行时看到了 IllegalAccessException 或 InaccessibleObjectException 消息,那么你可以使用 --add-opens 运行时选项,并根据异常消息中显示的信息来设置参数。

–add-opens 的语法如下:

--add-opens module/package=target-module(,target-module)*

此选项允许模块开放指定包给指定模块访问 ,无论模块声明如何。

作为一个特殊情况,如果 是 ALL-UNNAMED,那么源包将被导出到所有未命名的模块,无论它们最初是存在的,还是稍后创建的。例如:

--add-opens java.management/sun.management=ALL-UNNAMED

此示例允许类路径上的所有代码访问java.management/sun.management包中公共类型的非公共成员。

<!--在Java 11及更高版本中,当你需要在模块系统中允许类路径上的代码进行深度反射访问模块内部的非公开成员(比如私有或包访问权限的类、方法或字段)时,可以使用--add-opens命令行选项。以下是如何在编译时使用--add-opens的示例:假设你有一个模块com.example.mylibrary,你想允许类路径上的代码对其进行深度反射访问,你可以这样做: -->
javac --add-opens com.example.mylibrary/com.example.internal=ALL-UNNAMED \
      -cp your-classpath \
      YourJavaFile.java
<!--在这个命令中:•com.example.mylibrary是包含你想允许反射访问的内部包的模块名称。•com.example.internal是你希望开放反射权限的包名称。•ALL-UNNAMED表示允许所有不在模块系统内的代码进行深度反射访问。如果是其他模块,你可以将ALL-UNNAMED替换为具体的模块名称。
如果你使用Maven构建工具,并且使用maven-compiler-plugin插件,可以在插件配置中加入类似的参数:-->
<project>
  [...]
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <release>11</release>
          <compilerArgs>
            <!-- 允许反射访问 -->
            <arg>--add-opens</arg>
            <arg>com.example.mylibrary/com.example.internal=ALL-UNNAMED</arg>
          </compilerArgs>
        </configuration>
      </plugin>
    </plugins>
  </build>
  [...]
</project>
<!--需要注意的是,使用--add-opens打开了模块的封装边界,允许其他代码访问原本受限的部分。在生产环境中,这可能会降低安全性,因此应该谨慎使用,并尽量避免过度开放反射权限。在可能的情况下,应寻求使用API提供的公开功能,而不是依赖于反射。-->

注意:

如果您正在使用JNI调用API,包括,例如,Java Web Start JNLP文件,则必须在两者之间包含等号 ——add-opens以及它的值。

<j2se version="10" java-vm-args="--add-opens=module/package=ALL-UNNAMED"  />

在命令行中,–add-opens 及其值之间的等号(=)是可选的。

新版本字符串方案

JDK 10对JDK 9中引入的版本字符串模式进行了一些小的更改,以更好地适应基于时间的发布模型。JDK 11保留了JDK 10中引入的版本字符串格式。

如果您的代码依赖于版本字符串格式来区分主要、次要、安全和补丁更新版本,那么您可能需要更新它。

它主要用于检测和匹配Java版本,例如在自动化构建脚本、条件编译指令或者兼容性检查等方面。

新版本字符串的格式为:

$FEATURE.$INTERIM.$UPDATE.$PATCH

$FEATURE - 功能发布计数器,无论发布内容如何,每次功能发布都会增加。功能发布可能会添加新功能;如果提前至少一个功能发布提前通知,也可以删除功能。当合理时,可以进行不兼容的更改。

$INTERIM - 临时发布计数器,用于不包含不兼容更改、功能删除或标准API更改的兼容错误修复和增强的非功能发布。

$UPDATE - 更新发布计数器,用于修复安全问题、回归和新功能中的错误的兼容更新发布。

$PATCH - 紧急补丁发布计数器,仅当需要制作紧急发布以修复关键问题时才会增加。

添加了一个简单的Java API来解析、验证和比较版本字符串。看到 java.lang.Runtime.Version

Runtime.Version version = Runtime.version();
System.out.println(version);
//11.0.22+9-LTS-21

参见Java平台标准版安装指南中的版本字符串格式Version String Format

关于JDK 9中引入的版本字符串的更改,请参见JEP 223:新版本字符串方案 JEP 223: New Version-String Scheme

关于JDK 10中引入的版本字符串更改,请参见JEP 322:基于时间的发布版本控制JEP 322: Time-Based Release Versionin

更改已安装的JDK/JRE映像

对JDK和JRE进行了重大更改。

修改了JDK和JRE布局

在安装JDK之后,如果您查看文件系统,您会注意到目录布局与JDK 9之前的版本不同。

JDK 11

JDK 11没有JRE镜像。请参见《Java平台标准版安装指南》中“JDK安装目录结构”章节Installed Directory Structure of JDK

在Linux中安装JDK地址 单击这里

/安装目录
JDK软件安装的根目录。此目录还包含版权、README和发行说明文件。

/安装目录/Contents/Home/
macOS上JDK软件安装的根目录。

/安装目录/bin
由与映像链接的模块定义的可执行文件和命令行启动器。

/安装目录/conf
由开发人员、部署人员和最终用户编辑的.properties、.policy和其他配置文件。

/安装目录/lib
运行时系统的私有实现细节。这些文件不打算供外部使用,也不得修改。

在macOS上的lib目录或在Linux和Solaris上的lib/$ARCH目录包含运行时系统的动态链接本机库。

/安装目录/jmods
编译的模块定义。

/安装目录/legal
每个模块的版权和许可文件。

/安装目录/lib/src.zip
包含Java平台源代码的归档文件。

/安装目录/include
支持使用Java本机接口(JNI)和Java虚拟机(JVM)调试器接口的本机代码编程的C语言头文件。

这是JDK文件结构和目录的详细解释。JDK,即Java Development Kit,是Java开发者工具包,它提供了Java程序开发和运行所需的各种工具、库和运行时环境。每个目录和子目录都有其特定的用途和内容。

例如,/安装目录/bin 目录包含了Java运行时环境(JRE)的可执行文件,如 javajavac,它们用于运行和编译Java程序。/安装目录/lib 目录则包含了Java标准类库,这是Java程序运行所必需的。

/安装目录/include/安装目录/jmods 等目录则是为更高级的用途,如JNI编程或模块定义等提供的。

/安装目录/legal 目录包含了与JDK使用相关的版权和许可信息,这对于理解JDK的许可条款和遵守版权规定非常重要。

/安装目录/conf 目录则可能包含了一些配置文件,这些文件通常由JDK的用户或管理员进行编辑,以定制JDK的行为。

总的来说,理解JDK的文件结构和每个目录的用途,对于Java开发者和管理员来说都是非常重要的。这有助于他们更有效地使用JDK,理解其工作原理,以及更好地维护和调试Java程序。

JDK 9和JDK 10

以前的版本产生了两种类型的运行时映像:JRE,它是Java SE平台的完整实现,以及JDK,它在JRE /目录中包含了整个JRE,以及开发工具和库。

在JDK 9和JDK 10中,JDK和JRE是两种类型的模块化运行时映像,每种映像包含以下目录:

  • bin:包含二进制可执行文件。
  • conf:包含。properties、。policy和其他类型的文件,供开发人员、部署人员和最终用户编辑。这些文件以前在lib目录或其子目录中找到。
  • lib:包含动态链接的库和JDK的完整内部实现。

在JDK 9和JDK 10中,仍然有单独的JDK和JRE下载,但它们都具有相同的目录结构。JDK映像包含JDK中历史上发现的额外工具和库。没有jdk/与jre/ wrapper目录,并且二进制文件(例如java命令)不重复。

参见JEP 220:模块化运行时映像JEP 220: Modular Run-Time Images

新的类加载器实现

JDK 9及以后的版本维护了自1.2版本以来就存在的类加载器的层次结构。但是,为了实现模块系统,进行了以下更改:

  • 应用程序类加载器APPClassLoader不再是URLClassLoader的实例,而是内部类的实例。它是既不是Java SE也不是JDK模块的类的默认加载器。

  • 扩展类加载器已被重命名;现在它是平台类加载器。通过平台类加载器可以保证Java SE平台中的所有类都是可见的。

    仅仅因为类可以通过平台类加载器可见,并不意味着该类实际上是由平台类加载器定义的。Java SE平台中的一些类是由平台类加载器定义的,而其他类则是由引导类加载器定义的。应用程序不应依赖于哪个类加载器定义了哪个平台类。

    在JDK 9中实现的更改,可能会影响以null(即引导类加载器)为父类加载器的类加载器的代码,并假设所有平台类都对父类可见。此类代码可能需要更改为使用平台类加载器作为父类(参见ClassLoader.getPlatformClassLoader)。

    平台类加载器不是URLClassLoader的实例,而是内部类的实例。

  • 引导类加载器仍然内置于Java虚拟机中,并在ClassLoader API中用null表示。它定义了少量关键模块(如java.base)中的类。因此,它定义的类比JDK 8中的要少得多,因此使用-Xbootclasspath/a部署的应用程序或创建以null为父类的类加载器的应用程序可能需要按照前面所述进行更改。

SystemClassLoader/APPClassLoader【应用程序类加载器:即ClassLoader.getSystemClassLoader()】同一个意思

null【BootstrapClassLoader:引导类加载器】

PlatformClassLoader【扩展类加载器重命名为平台类加载器】

这一段可跳过【学习使用】
类加载器是负责加载类的对象。ClassLoader类是一个抽象类。给定类的二进制名称,类加载器应尝试定位或生成构成该类定义的数据。一种典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的“类文件”。

每个Class对象都包含对定义它的ClassLoader的引用。

数组类的类对象不是由类加载器创建的,而是在Java运行时需要时自动创建的。对于数组类,由Class.getClassLoader()返回的类加载器与其元素类型的类加载器是相同的;如果元素类型是原始类型,则数组类没有类加载器

应用程序实现ClassLoader的子类,以便扩展Java虚拟机动态加载类的方式。

类加载器通常可由安全管理器使用,以指示安全域。

除了加载类,类加载器还负责定位资源。资源是一些数据(例如“.class”文件、配置数据或图像),它们使用抽象的“/”分隔的路径名进行标识。资源通常与应用程序或库一起打包,以便应用程序或库中的代码可以找到它们。在某些情况下,资源包括在内,以便其他库可以找到它们。

URL systemResource = ClassLoader.getSystemResource(“java/lang/Class.class”);

ClassLoader类使用委托模型来搜索类和资源。每个ClassLoader实例都有一个关联的父类加载器。当请求查找类或资源时,ClassLoader实例通常会将查找类或资源的任务委托给其父类加载器,然后再尝试自己查找类或资源。

支持并发加载类的类加载器被称为并行兼容的类加载器,并需要在类初始化时通过调用ClassLoader.registerAsParallelCapable方法将自己注册为并行兼容的类加载器。请注意,ClassLoader类默认注册为并行兼容的类加载器。但是,如果它的子类也是并行兼容的,则仍然需要将自己注册为并行兼容的类加载器。在委托模型不是严格层次结构的环境中,类加载器需要是并行兼容的,否则类加载可能导致死锁,因为加载器锁在类加载过程期间保持锁定(参见loadClass方法)。

这一段可跳过【学习使用】
运行时内置类加载器

Java运行时具有以下内置类加载器:

  1. 引导类加载器Bootstrap class loader。它是虚拟机的内置类加载器,通常表示为null,并且没有父加载器。
  2. 平台类加载器Platform class loader。所有平台类对平台类加载器都可见,可作为ClassLoader实例的父加载器。平台类包括Java SE平台API、它们的实现类以及由平台类加载器或其祖先定义的特定于JDK的运行时类。

为了允许升级/覆盖平台类加载器定义的模块,并且在升级模块读取平台类加载器及其祖先之外的其他类加载器定义的模块时,平台类加载器可能需要委托给其他类加载器,例如应用程序类加载器。换句话说,对于平台类加载器及其祖先之外的其他类加载器定义的命名模块中的类,平台类加载器可能可见。

  1. 系统类加载器System class loader。它也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径、模块路径和特定于JDK的工具上的类。平台类加载器是系统类加载器的父加载器或祖先加载器,所有平台类都对其可见。

通常,Java虚拟机以与平台相关的方式从本地文件系统中加载类。然而,某些类可能不是从文件中产生的;它们可能来自其他来源,例如网络,或者它们可以由应用程序构造。defineClass方法将字节数组转换为Class类的实例。可以使用Class.newInstance创建这个新定义的类的实例

类加载器创建的对象的方法和构造函数可能引用其他类。为了确定引用的类,Java虚拟机调用最初创建该类的类加载器的loadClass方法。

这一段可跳过【学习使用】

ClassLoader.getSystemClassLoader()

返回系统类加载器。这是新的ClassLoader实例的默认委托父类,并且通常是用于启动应用程序的类加载器。
此方法在运行时启动序列的早期被首次调用,此时它创建系统类加载器。

此类加载器将是主应用程序线程(例如,调用主类主方法的线程)的上下文类加载器。

默认的系统类加载器是此类的一个实现依赖的实例。

如果首次调用此方法时定义了系统属性"java.system.class.loader",则该属性的值将被视为将作为系统类加载器返回的类的名称。该类使用默认的系统类加载器进行加载,并且必须定义一个接受类型为ClassLoader的单个参数的公共构造函数,该构造函数用作委托父类。然后使用此构造函数和默认的系统类加载器作为参数创建实例。将定义的类加载器定义为系统类加载器。在构造过程中,类加载器应非常小心,避免调用getSystemClassLoader()。如果检测到系统类加载器的循环初始化,则抛出IllegalStateException。

实现说明:
直到虚拟机几乎完全初始化,才会检查用于覆盖系统类加载器的系统属性。

在启动过程中执行此方法的代码应注意,直到系统完全初始化,才不要缓存返回值。
内置系统类加载器的名称是"app"。在VM的早期初始化过程中读取系统属性"java.class.path"以确定类路径。如果"java.class.path"属性的值为空,则根据初始模块(包含主类的模块)是否有名称,其解释方式会有所不同:如果已命名,内置的系统类加载器将不具有类路径,并将使用应用程序模块路径搜索类和资源;否则,如果未命名,它将把类路径设置为当前工作目录。

删除了JDK 9中的rt.jar和tools.jar

先前存储在 lib/rt.jar、lib/tools.jar、lib/dt.jar 和各种其他内部 JAR 文件中的类和资源文件现在以更高效的格式存储在 lib 目录中的特定实现文件中。

移除 rt.jar 和类似文件会导致以下领域出现问题:

  • 从JDK 9开始,ClassLoader.getSystemResource不再返回指向JAR文件的URL(因为没有JAR文件)。相反,它返回一个jrt URL,该URL命名存储在运行时映像中的模块、类和资源,而不揭示映像的内部结构或格式。

    例如:

    ClassLoader.getSystemResource("java/lang/Class.class");
    

    当在JDK 8上运行时,这个方法返回如下形式的JAR URL:

    jar:file:/usr/local/jdk8/jre/lib/rt.jar!/java/lang/Class.class
    

    它嵌入一个文件URL来命名运行时映像中的实际JAR文件。

    模块化映像不包含任何JAR文件,因此这种形式的url没有意义。在JDK 9及以后的版本中,这个方法返回:

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

    文档补充:

    在Java 9及其后续版本中,jrt-fs.jar 是Java运行时镜像(JRT)文件系统(JRT FileSystem)的实现。全称可以理解为“Java Run-Time FileSystem JAR”。

    jrt-fs.jar 主要作用在于提供对Java运行时模块系统(Module System)的模块路径(module path)的访问支持。

    Java 9引入了模块化系统,将Java平台API和其他库组织为模块,每个模块的类和资源都被存储在一个紧凑高效的运行时镜像(Jimage)格式中。

    jrt-fs.jar 实现了一个文件系统接口,允许Java程序像访问普通文件系统一样访问这些模块内的类和资源,而无需将它们物理地解压或展开到文件系统中。

    在Java 11中,当你运行一个Java应用程序时,Java虚拟机(JVM)会使用jrt-fs.jar 来加载模块化的Java运行时库,

    而不是像过去那样从传统的rt.jar 或其他多个JAR文件中加载。这样做的好处包括更高效的空间占用和更快的类加载速度。

    同时,jrt-fs.jar 的存在也是为了支持通过文件系统API来探索和访问模块内容。

    java.security.CodeSource API 和安全策略文件使用 URL 来命名将被授予特定权限的代码库的位置。

    请参阅 Java Platform, Standard Edition Security Developer’s Guide 中的“政策文件语法”Policy File Syntax

    目前,需要特定权限的运行时系统组件通过使用文件 URL 在 conf/security/java.policy 文件中进行标识。

    较旧版本的集成开发环境(IDE)和其他开发工具需要能够枚举存储在运行时映像中的类和资源文件,并通过打开和读取 rt.jar 和类似文件直接读取其内容。这在模块化映像中是不可能的。

删除了JDK 9中的扩展机制

在JDK 8及更早的版本中,扩展机制使运行时环境能够查找和加载扩展类,而无需在类路径中特别命名它们。

从JDK 9开始,如果需要使用扩展类,请确保JAR文件位于类路径上。

在JDK 9和JDK 10中,如果设置了java.ext.dirs系统属性,或者存在lib/ext目录,则javac编译器和java启动器将退出。

要额外检查特定于平台的系统范围目录,请指定-XX:+CheckEndorsedAndExtDirs命令行选项。如果目录存在且不为空,则会导致相同的退出行为。

但是,在JDK 11中,-XX:+CheckEndorsedAndExtDirs命令行选项已经过时了,并且在使用它时会发出警告。

扩展类加载器在JDK 9(及以后的版本)中保留,在java 11中被重命名为平台类加载器(参见getPlatformClassLoader)。

下面的错误意味着你的系统被配置为使用扩展机制:

<JAVA_HOME>/lib/ext exists, extensions mechanism no longer supported; Use -classpath instead.
.Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
< JAVA_HOME >/lib/ext存在,扩展机制不再支持;错误:无法创建Java虚拟机。错误:发生了致命异常。程序将退出。

如果设置了java.ext.dirs系统属性,您将看到类似的错误。

要修复此错误,请删除ext/目录或java.ext.dirs系统属性。

参见JEP 220:模块化运行时映像 JEP 220: Modular Run-Time Images.。

删除了认可标准覆盖机制

Removed Endorsed Standards Override Mechanism。

java. endorsed.dirs系统属性和lib/ endorsed目录不再存在。如果检测到其中一个,javac编译器和java启动器将退出。

从JDK 9开始,您可以使用可升级的模块,或者将JAR文件放在类路径上。

该机制旨在让应用程序服务器覆盖JDK中使用的组件。要更新的包将放在JAR文件中,系统属性java .endorsed.dirs将告诉Java运行时环境在哪里可以找到它们。如果未指定此属性的值,则使用默认的$JAVA_HOME/lib/endorsed。

在JDK 8中,您可以使用-XX:+CheckEndorsedAndExtDirs命令行参数来检查系统中任何位置的此类目录。

在JDK 9及以后的版本中,如果设置了java.endorsed.dirs系统属性,或者存在lib/endorsed目录,javac编译器和java启动器将退出。

下面的错误意味着你的系统被配置为使用认可的标准覆盖机制:

< JAVA_HOME >不支持/lib/endorsed。模块化形式的认可标准和独立api将通过可升级模块的概念得到支持。
错误:无法创建Java虚拟机。错误:发生了致命异常。程序将退出。

如果设置了java.endorsed.dirs系统属性,您将看到类似的错误。

要修复此错误,请删除lib/endorsed目录,或取消设置java.endorsed.dirs系统属性。

参见JEP 220:模块化运行时映像。

Windows注册表项更改

Java 11安装程序在安装JDK时会创建这些Windows注册表项:

  • “HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK”
  • “HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\11”

如果安装了两个版本的JDK,那么将创建两个不同的Windows注册表项。例如,如果JDK 11.0.1与JDK 11一起安装,那么安装程序会创建另一个Windows注册表项,如下所示:

  • “HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK”
  • “HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\11.0.1”

删除或更改的api

本节重点介绍那些已被禁用、删除或更改其默认行为的api。在编译或运行应用程序时,您可能会遇到本节中描述的问题。

参见JDK 11中已删除的api,Removed APIs in JDK 11

JDK 9和JDK 10中删除的api

以下是从JDK 9和JDK 10版本中删除的一些重要api。

java.* APIs中的移除

Java团队致力于向后兼容性。如果一个应用程序在JDK 8中运行,那么只要它使用受支持的、供外部使用的api,它就可以在JDK 9和更高版本上运行。

这些包括:

  • JCP标准,java.*, javax . *

    Java Community Process (JCP) 是一个制定Java技术标准的开放性流程,由甲骨文公司领导和管理。

    JCP的目标是通过标准化过程协调Java生态系统中的各方利益,确保Java平台的兼容性和一致性发展。

    JCP负责审批和维护Java规范请求(Java Specification Requests, JSRs),

    这些JSRs最终形成了Java平台的各种标准和API,例如Java SE、Java EE(现在为Jakarta EE)以及其他技术规范。

  • 特定于jdk的api,一些com.sun.*,jdk.*

一些jdk支持的api可以从JDK中删除,但必须事先通知。通过运行静态分析工具jdeprscan来确定代码是否使用了不推荐的api

在JDK 9中被移除的java.* APIs包括java.util.logging.LogManager和java.util.jar.Pack200包中以前已弃用的方法:

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

sun.misc和sun.reflect API的移除和未来移除

不同于java.* API,几乎所有的sun.* API都是不受支持的、JDK内部API,并且可能会在任何时候消失。

在JDK 9中,一些sun.* API已被移除。

值得注意的是,sun.misc.BASE64Encoder和sun.misc.BASE64Decoder已被移除。相反,应使用在JDK 8中添加的受支持的java.util.Base64类。

如果您使用这些API,您可能希望迁移到其受支持的替代方案:

  • sun.misc.Unsafe 此类中许多方法的功能可以通过使用变量句柄来获得,请参阅JEP 193:变量句柄 JEP 193: Variable Handles.。
  • sun.reflect.Reflection::getCallerClass(int) 请使用堆栈遍历API替代,请参阅JEP 259:堆栈遍历APIJEP 259: Stack-Walking API.。

请参阅JEP 260:封装大多数内部API JEP 260: Encapsulate Most Internal APIs.。

java.awt.peer不可访问

从JDK 9开始,java.awt.peerjava.awt.dnd.peer包不可访问。尽管这些包位于java.*命名空间中,但它们从未成为Java SE API的一部分。

在Java SE API中引用这些包中定义的类型的所有方法已从JDK 9中删除。调用以前接受或返回这些包中定义的类型的方法的代码不再编译或运行。

java.awt.peer类有两种常见的用途。您应该按照以下方式替换它们:

  • 查看是否已设置a peer :

    if (component.getPeer() != null) { .. }
    

    使用JDK 1.1 API中的Component.isDisplayable()替换它:

    public boolean isDisplayable() {
    	return getPeer() != null;
    }
    
  • 要测试一个组件是否轻量级:

    if (component.getPeer() instanceof LightweightPeer)…
    

    使用JDK 1.2 API中的Component.isLightweight()方法替换它:

    public boolean isLightweight() {
    	return getPeer() instanceof LightweightPeer;
    }	
    

删除了com.sun.image.codec.jpeg包

非标准包com.sun.image.codec.jpeg已被删除。请使用Java Image I/O API。

JPEG包是在JDK 1.2中添加的,作为控制JPEG格式图像文件加载和保存的非标准方式。它从未成为平台规范的一部分。

在JDK 1.4中,Java Image I/O API作为标准API被添加到javax.imageio包中。

它提供了一种控制加载和保存采样图像格式的标准机制,并要求所有兼容的Java SE实现支持基于Java image I/O规范的JPEG。

删除了对紧凑配置文件的工具支持

从JDK 9开始,您可以选择在Java运行时镜像中的任何模块子集上构建和运行应用程序,而无需依赖于预定义的配置文件。

在Java SE 8中引入的概要文件定义了Java SE Platform API的子集,这些子集可以减少存储容量有限的设备上Java运行时的静态大小。JDK 8中的工具支持三个配置文件:compact1、compact2和compact3。关于每个配置文件的API组成,请参见JDK 8文档中的详细配置文件组成和API参考。

在JDK 8中,您可以使用-profile选项在运行javac和java命令时指定配置文件。从JDK 9开始,-profile选项仅与–release 8选项一起由javac支持,而不被java支持。

JDK 9及更高版本允许您选择在编译和运行时使用的模块。通过使用新的**–limit-modules**选项指定模块,您可以获得与紧凑配置文件相同的API。此选项既支持javac命令,也支持java命令,如下所示:

//javac
javac --limit-modules java.base,java.logging MyApp.java
//java
java --limit-modules java.base,java.logging MyApp

在Java SE 8中,针对每个配置文件指定的包都会由以下模块集共同导出:

对于compact1配置文件:java.base,java.logging,java.scripting

对于compact2配置文件:java.base,java.logging,java.scripting,java.rmi,java.sql,java.xml

对于compact3配置文件:java.base,java.logging,java.scripting,java.rmi,java.sql,java.xml,java.compiler,java.instrument,java.management,java.naming,java.prefs,java.security.jgss,java.security.sasl,java.sql.rowset,java.xml.crypto

您可以使用jdeps工具对源代码中使用的Java包进行静态分析。这可以为您提供执行应用程序所需的模块集。例如,如果您使用的是compact3配置文件,那么您可能会发现,在构建应用程序时,您不需要包含整个模块集。请参阅Java Platform,Standard Edition Tools Reference中的jdeps

请参阅 JEP 200: The Modular JDK

默认使用CLDR本地化设置数据

从JDK 9开始,Unicode Consortium的Common Locale Data Repository(CLDR)数据被设置为默认的本地化数据,这样你就可以在不进行任何额外操作的情况下使用标准的本地化数据。

在JDK 8中,尽管CLDR本地化数据被捆绑在JRE中,但它默认并未启用。

使用本地化敏感服务(如日期、时间和数字格式化)的代码在使用CLDR本地化数据时可能会产生不同的结果。

记住,即使是System.out.printf()也是本地化感知的。

要使行为与JDK 8兼容,请将系统属性java.locale.providers设置为以COMPAT开头的值,例如java.locale.providers=COMPAT,CLDR

在Java Platform,Standard Edition国际化指南和JEP 252:默认使用CLDR本地化数据中,请查看默认启用的CLDR本地化数据。

JEP 252: Use CLDR Locale Data by Default

CLDR Locale Data Enabled by Default

部署

Java部署技术在JDK 9中已弃用,并在JDK 11中删除。

Java部署技术是指以下几个工具包:
以下工具可以让你在web上部署Java应用程序和小程序:

  • pack200:使用pack200命令使用Java gzip压缩器将Java Archive (JAR)文件转换为压缩后的pack200文件。【Java 11中被标记为过时】
  • unpack200:使用unpack200命令将打包的文件转换为用于web部署的JAR文件。【Java 11中被标记为过时】
  • javapackager:使用javapackager命令执行与打包Java和JavaFX应用程序相关的任务。【Java 11中被删除】

使用随JDK 9引入的jlink工具来打包和部署专用运行时环境,而不是依赖预安装的JRE系统。

删除了启动时JRE版本选择

从JDK 9开始,在启动时请求不是正在启动的JRE版本的JRE的能力被移除了。【换句话说:如果启动时给定了JRE版本就报错】

现代应用程序通常使用Java Web Start(JNLP)、原生操作系统打包系统或活动安装程序进行部署。这些技术有自己的方法来管理所需的JRE,通过查找或下载和更新所需的JRE,按需进行。这使得启动器的启动时JRE版本选择变得过时。

在之前的版本中,您可以在启动应用程序时指定要使用的JRE版本(或版本范围)。可以通过命令行选项和应用程序JAR文件中的清单条目来选择版本。

从JDK 9开始,对java启动器进行了以下修改:

  • 如果在命令行上给定-version:选项,则发出错误消息并退出。
  • 如果在JAR文件中找到JRE-Version清单条目,则发出警告消息并继续。

请参阅JEP 231:删除启动时JRE版本选择。

删除了对序列化小程序的支持

从JDK 9开始,将applet作为序列化对象进行部署的能力不再受支持。使用现代压缩和JVM性能来部署applet没有任何好处。

在启动applet时,将忽略applet标签的对象属性以及object和java object applet参数标签。

不要序列化applet,而是使用标准的部署策略。

JNLP规范更新

Java网络启动协议(JNLP)已更新,以消除不一致性,使代码维护更容易,并增强安全性。

JNLP的更新如下:

在JNLP文件中,使用&amp;代替&。

JNLP文件语法符合XML规范,所有JNLP文件都应能够由标准XML解析器解析。

JNLP文件允许您指定复杂的比较。以前,这是通过使用&符号来完成的,但这在标准XML中不受支持。如果您使用&来创建复杂的比较,那么在您的JNLP文件中,请将其替换为&amp;。&amp;与所有版本的JNLP都兼容。

比较数值版本元素类型与非数值版本元素类型。

以前,当int版本元素与另一个无法解析为int的版本元素进行比较时,将按照ASCII值进行字典顺序比较版本元素。

从JDK 9开始,如果可解析为int的元素比另一个元素短,则会在比较其ASCII值的字典顺序之前,在其前面填充前导零。这确保了没有循环性。

在同时使用版本比较和JNLP servlet的情况下,您应该只使用数值来表示版本。

在java(或j2se)元素中具有嵌套资源的组件扩展。

这在规范中是允许的。虽然以前支持,但这种支持并没有反映在规范中。

FX XML扩展。

JNLP规范已得到增强,以为application-desc元素添加了type属性,并在application-desc中添加了子元素param(正如它在applet-desc中一样)。

这不会给现有应用程序带来问题,因为以前指定JavaFX应用程序的方式仍然受支持。

请参阅JSR-056中的JNLP规范更新JSR-056

JDK 9中的安全更新

从JDK 9开始,一些与安全性相关的默认值发生了变化。

Java加密扩展:JCE管辖权策略文件默认为无限制

如果您的应用程序以前需要Java加密扩展Java Cryptography Extension (JCE) 无限强度管辖权策略文件,那么您不再需要下载或安装它们。它们包含在JDK中,默认情况下是激活的。

如果您的国家或使用情况需要更严格的策略,那么有限的Java加密策略文件仍然可用。

如果您对默认提供的策略文件有任何不满足的需求,那么您可以定制这些策略文件以满足您的需求。

请参阅/conf/security/java.security文件中的crypto.policy安全属性,或Java平台标准版安全开发人员指南中的加密强度配置。

建议您咨询出口/进口管制顾问或律师,以确定确切的需求。

创建PKCS12密钥库

我们建议您对密钥库使用PKCS12格式。这种格式是默认的密钥库类型,它基于RSA PKCS12个人信息交换语法标准。

参见在Java平台中创建与JSSE一起使用的密钥库 Creating a Keystore to Use with JSSE,标准版安全开发人员指南和Java平台中的 keytool,标准版工具参考。

对垃圾收集的更改

本节描述从JDK 9开始对垃圾收集所做的更改。

将G1设置为默认垃圾收集器

Garbage-First垃圾收集器(G1 GC)是JDK 9及后续版本的默认垃圾收集器。

对于大多数用户来说,像G1 GC这样的低停顿收集器应该比JDK 8默认的吞吐量导向型收集器(如Parallel GC)提供更好的整体体验。

要了解有关调整G1 GC的更多信息,请参阅Java平台标准版Java虚拟机指南中的G1 GC的默认设置 Ergonomic Defaults for G1 GC和可调默认值Tunable Defaults

删除GC选项

以下GC组合将导致您的应用程序在JDK 9和更高版本中无法启动:

  • DefNew + CMS
  • ParNew + SerialOld
  • Incremental CMS

CMS的前景模式也被删除了。被删除的命令行标志是

  • xingc, -XX:+CMSIncrementalMode, -XX:+UseCMSCompactAtFullCollection,
  • -XX:+CMSFullGCsBeforeCompaction和-XX:+UseCMSCollectionPassing。

命令行标志-XX:+UseParNewGC不再生效。ParNew标志只能与CMS一起使用,而CMS需要ParNew。

因此,-XX:+UseParNewGC标志已被弃用,并可能在将来的版本中移除。

参见JEP 214:删除JDK 8中已弃用的GC组合 JEP 214: Remove GC Combinations Deprecated in JDK 8

移除永久代

在JDK 8中删除了永久代,并且相关的VM选项会导致打印警告。你应该从脚本中删除这些选项:

  • - xx: MaxPermSize =size
  • - xx: PermSize =size

在JDK 9及以后的版本中,JVM会显示如下警告:

Java HotSpot(TM) 64位服务器虚拟机警告:忽略选项MaxPermSize;支持在8.0中被移除

意识到永久代的工具可能必须更新。

参见JEP 122:删除永久生成和JDK 9发行说明-删除的api,功能和选项。

JEP 122: Remove the Permanent Generation and JDK 9 Release Notes - Removed APIs, Features, and Options .

对GC日志输出的更改【重要】

垃圾收集(GC)日志记录使用JVM统一的日志记录框架,新旧日志之间存在一些差异。您正在使用的任何GC日志解析器可能都需要更改。

您可能还需要更新JVM日志记录选项。所有与GC相关的日志记录都应该使用gc标签(例如,-Xlog:gc),通常与其他标签结合使用。

-XX:+PrintGCDetails和-XX:+PrintGC选项已被弃用。

请参阅Java Platform, Standard Edition Tools Reference中的《使用JVM统一日志记录框架启用日志记录》和JEP 271:统一的GC日志记录。

Enable Logging with the JVM Unified Logging Framework

JEP 271: Unified GC Logging.

移除工具和组件

此列表包括不再与JDK捆绑在一起的工具和组件。

要了解JDK 11中删除的工具和组件的更多信息,请参见JDK 11中已删除的api。

删除本机头生成工具(javah)

javah工具已经被javac中的高级功能所取代。它在JDK 10中被删除了。

从JDK 8开始,javac提供了在编译Java源代码时编写本机头文件的能力,从而消除了对单独工具的需求。

使用 javac - h 替代 `javah`工具

删除JavaDB

JavaDB,这是Apache Derby的一个重新包装的品牌,不再包含在JDK中。

JavaDB被捆绑在JDK 7和JDK 8中。它在JDK安装目录的db目录中找到。

您可以从Apache Derby下载页面下载并安装Apache Derby。

删除了JVM TI hprof Agent

hprof代理库已被移除。

hprof代理是为JVM工具接口编写的演示代码,并非用于生产环境。hprof代理的有用功能已被更好的替代方案所取代,其中包括JDK中的一些替代方案。

要创建hprof格式的堆转储,请使用诊断命令(jcmd)或jmap工具:

  • jcmd GC.heap_dump。请参阅jcmd
  • jmap:jmap -dump。请参阅jmap

对于CPU分析器功能,请使用随JDK捆绑的Java Flight Recorder。

注意:Java Flight Recorder在生产环境中使用需要商业许可证。要了解有关商业功能和如何启用它们的更多信息,请访问http://www.oracle.com/technetwork/java/javaseproducts/。

请参阅JEP 240:移除JVM TI hprof代理 JEP 240: Remove the JVM TI hprof Agent

删除了jhat工具

jhat工具是JDK 6中添加的一个实验性、不受支持的堆可视化工具。多年来,已经出现了更高级的堆可视化和分析工具。

删除了java-rmi.exe和java-rmi.cgi启动器

Windows 上的 java-rmi.exe 和 Linux 以及 Solaris 上的 java-rmi.cgi 启动器已被删除。

在 Linux 和 Solaris 上,java-rmi.cgi 位于 $JAVA_HOME/bin 目录中。

在 Windows 上,java-rmi.exe 位于 $JAVA_HOME/bin 目录中。

这些启动器被添加到 JDK 中,以方便使用已在 JDK 8 中弃用的 RMI CGI 代理机制。

使用 servlet 通过 HTTP 代理 RMI 的替代方案已经存在,甚至被优先考虑了几年。请参阅 Java RMI 和对象序列化。

从JMX rmicconnector中删除了对IIOP传输的支持

来自JMX RMI Connector的IIOP传输支持及其支持类已经从JDK中删除。

在JDK 8中,对IIOP传输的支持从必需降为可选。这是从JMX Remote API中移除对IIOP传输支持的多版本工作的第一步。在JDK 9中,完全删除了对IIOP的支持。

公共API的变化包括:

  • javax.management.remote.rmi.RMIIIOPServerImpl类已弃用。在调用时,它的所有方法和构造函数都会抛出带有解释性消息的java.lang.UnsupportedOperationException。
  • 两个类,org.omg.stub.javax.management.rmi。_RMIConnection_Stub和org.omg.stub.javax.management.rmi。_RMIConnection_Tie,没有生成。

丢弃Windows 32位客户端虚拟机

丢弃了Windows 32位客户端虚拟机 Windows 32位客户端虚拟机不再可用。只提供服务器虚拟机。

JDK 8及更早版本为Windows 32位系统提供了客户端JVM和服务器JVM。JDK 9及更高版本仅提供服务器JVM,它已调整以最大化峰值操作速度。

删除Java VisualVM

Java VisualVM是一个提供Java虚拟机上运行的代码信息的工具。JDK 6、JDK 7和JDK 8提供了jvisualvm工具。

Java VisualVM不再与JDK捆绑在一起,但是您可以从VisualVM开源项目站点获得它VisualVM open source project site

删除了native2ascii工具

已从JDK中移除native2ascii工具。由于JDK 9及以后的版本支持基于UTF-8的属性资源包,因此不再需要将基于UTF-8的属性资源包转换为ISO-8859-1的工具。

参见Java平台中的UTF-8属性文件,标准版国际化指南。

删除了macos特有的功能

本节包括从JDK 9开始删除的macos特定特性。

特定于平台的桌面功能

java.awt.Desktop类包含apple特定的com.apple.eawt和com.apple.eio包中api的替换。新的api取代了macOS api,并且是平台独立的。

com.apple.eawt和com.apple.eio包中的api是封装的,因此您将无法在JDK 9或更高版本中针对它们进行编译。但是,它们在运行时仍然可以访问,因此编译为旧版本的现有代码将继续运行。最终,使用apple和com中的内部类的库或应用程序。apple包及其子包需要迁移到新的API。

com.apple.concurrent和apple。Applescript包被删除,不做任何替换。

参见JEP 272:特定于平台的桌面功能。

移除AppleScript引擎

AppleScript引擎,一个平台特定的javax。脚本实现,已被删除,在JDK中没有任何替换。

AppleScript引擎在最近的版本中几乎无法使用。该功能仅在JDK 7或JDK 8中工作,并且系统上已经有Apple版本的applescriptenengine .jar文件。

运行Java小程序

如果您仍然依赖小程序,则可以在Windows系统上通过Internet Explorer模式下使用带有Microsoft Edge的JRE 8来启动它们。参见Microsoft Edge + Internet Explorer模式:入门指南。

截至2021年9月,启动applet所需的Java插件仍然在Java 8的Windows上更新,但可能会在未来的更新版本中随时删除。

Oracle客户可以在My.Oracle.Support Note 251148.1 - Java SE 8终止对Java插件的支持(需要登录)中找到更多信息。

正则表达式匹配中的行为改变

java.util.regex.Pattern 类在正则表达式中用方括号定义字符类。例如,[abc] 匹配 abc。否定字符类是在开方括号后紧跟一个脱字符(^)来定义的。例如,[^abc] 匹配除 abc 以外的任何字符。

在 JDK 8 及更早版本中,否定字符类不会否定嵌套的字符类。例如,[^a-b[c-d]e-f] 匹配 c 但不匹配 ae,因为它们不在嵌套类中。运算符是按顺序应用的。在这个例子中,否定运算符 ^ 在嵌套之前应用。在 JDK 8 及更早版本中,运算符 ^ 仅应用于字符类中的最外层字符,而不应用于嵌套的字符类。这种行为令人困惑且难以理解。

然而,在 JDK 9 及更高版本中,否定运算符应用于所有嵌套的字符类。例如,[^a-b[c-d]e-f] 不匹配 c

为了进一步解释,请考虑以下正则表达式:

//[^a-d&&c-f]
public static void main(String[] args) throws Exception {
    Pattern pattern = Pattern.compile("[^a-d&&c-f]");
    Matcher matcher = pattern.matcher("a");
    System.out.println(matcher.matches());//true
} 
a、b、c、d
     c、d、e、f

在JDK 8中,首先应用^运算符,因此这个示例被解释为[^a-d]与[c-f]的交集。这匹配e和f,但不匹配a、b、c或d。

在JDK 9及更高版本中,首先应用**&&**运算符,因此这个示例被解释为[a-d]&&[c-f]的补集。这匹配a、b、e和f,但不匹配c或d。

作为一种最佳实践,寻找使用字符类与否定、交集和嵌套类组合的正则表达式。可能需要调整这些正则表达式以适应更改后的行为。

下一个步骤

在JDK 11上运行应用程序之后,这里有一些建议可以帮助您从Java SE平台中获得最大收益:

  • 如果需要,使用javac工具中的新——release标志交叉编译到平台的旧版本。
  • 利用IDE的建议,用最新的特性更新代码。
  • 通过运行静态分析工具jdeprscan来确定代码是否使用了不推荐的api。正如本指南中已经提到的,api可以从JDK中删除,但必须事先通知。
  • 熟悉多版本JAR文件等新特性(参见JAR)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值