Java 10 已正式发布!
为了更快地迭代,以及跟进社区反馈,Java 的版本发布周期变更为了每六个月一次,并且承诺不会跳票。新的发布周期也会严格遵循时间点,将在每年的 3 月份和 9 月份发布。此次 Java 10 的版本号是 18.3。
Java 10 是采用新发布周期的第一个版本,提供了 109 项新特性,其中最受关注的莫过于包括局部变量的类型推断所在内的 12 项关键新特性。
Java 10 的 12 项关键新特性
JEP 286:局部变量类型推断
对于开发者来说,这是 JDK 10 唯一的真正特性。它向 Java 中引入在其他语言中很常见的 var,比如 JavaScript 。只要编译器可以推断此种类型,你就不再需要专门声明一个局部变量的类型。一个简单的例子是:
var x = new ArrayList();
这就消除了我们之前必须执行的 ArrayList 类型定义的重复。我鼓励你们去读 JEP ,因为上面有一些关于这个句法是否能用的规则。
有趣的是,需要注意 var 不能成为一个关键字,而是一个保留字。这意味着你仍然可以使用 var 作为一个变量、方法或包名,但是现在不能再调用类。
JEP 310:应用类数据共享(CDS)
CDS 在 JDK 5 时被引进以改善 JVM 启动的表现,同时减少当多个虚拟机在同一个物理或虚拟的机器上运行时的资源占用。
JDK 10 将扩展 CDS 到允许内部系统的类加载器、内部平台的类加载器和自定义类加载器来加载获得的类。之前,CDS 的使用仅仅限制在了 bootstrap 的类加载器。
JEP 314:额外的 Unicode 语言标签扩展
这将改善 java.util.Locale 类和相关的 API 以实现额外 BCP 47 语言标签的 Unicode 扩展。尤其是货币类型,区域覆盖和时区等标签现在将被支持。
JEP 322:基于时间的版本控制
JDK 版本字符串格式几乎与 JDK 版本一样多。有幸的是,这是最后需要使用到的,我们可以坚持用它。这种格式使用起来很像 JDK 9 中介绍的提供一个更加语义的形式。但其中包含了一个 INTERIM 元素,正如 JEP 提议中所说的“永远是 0”。好吧,如果永远是 0,那它有什么意义呢?他们说这是为未来使用做保留,但我仍不是很赞同。我认为,这有些冗余繁杂。
这也消除了在 JDK 9 中有过的相当奇怪的情形。第一次更新是 JDK 9.0.1 , 非常符合逻辑。第二次更新是 JDK 9.0.4 ,不合逻辑。原因是,在 JDK 9 的版本计数模式下,需要留下空白以便应急或不在预期安排的更新使用。但既然没有更新是必须的,为什么不简单称之为 JDK 9.0.2 呢?
JEP 319:根证书
在 JDK 中将提供一套默认的 CA 根证书。关键的安全部件,如 TLS ,在 OpenJDK 构建中将默认有效。Oracle 正在努力确保 OpenJDK 二进制和 Oracle JDK 二进制功能一致,这是一项有用的补充内容。
JEP307:并行全垃圾回收器 G1
G1 是设计来作为一种低延时的垃圾回收器(但是如果它跟不上旧的堆碎片产生的提升速率的话,将仍然采用完整压缩集合)。在 JDK 9 之前,默认的收集器是并行、吞吐、收集器。为了减少在使用默认的收集器的应用性能配置文件的差异,G1 现在有一个并行完整收集机制。
JEP 313:移除 Native-Header 自动生成工具
Java 9 开始了一些对 JDK 的家务管理,这项特性是对它的延续。当编译 JNI 代码时,已不再需要单独的工具来生成头文件,可以通过 javac 完成。在未来的某一时刻,JNI 将被 Panama 项目的结果取代。
JEP 304:垃圾回收器接口
这不是让开发者用来控制垃圾回收的接口,而是一个在 JVM 源代码中的允许另外的垃圾回收器快速方便的集成的接口。
JEP 312:线程—局部变量管控
这是在 JVM 内部相当低级别的更改,现在将允许在不运行全局虚拟机安全点的情况下实现线程回调。这将使得停止单个线程变得可能和便宜,而不是只能启用或停止所有线程。
JEP316:在备用存储装置上的堆分配
硬件技术在持续进化,现在可以使用与传统 DRAM 具有相同接口和类似性能特点的非易失性 RAM 。这项 JEP 将使得 JVM 能够使用适用于不同类型的存储机制的堆。
JEP317:试验性的基于 Java 的 JIT 编译器
最近宣布的 Metropolis 项目提议用 Java 重写大部分 JVM,乍一看觉得很奇怪。如果 JVM 是用 Java 编写的,那么是否需要一个 JVM 来运行 JVM ? 相应的,这导致了一个很好的镜像类比。现实情况是,使用 Java 编写 JVM 并不意味着必须将其编译为字节码,你可以使用 AOT 编译,然后在运行时编译代码以提高性能。
这项 JEP 将 Graal 编译器研究项目引入到 JDK 中,并让 Metropolis 项目成为现实,使 JVM 性能与当前 C++ 所写版本的匹敌提供了基础。
JEP296:合并 JDK 多个代码仓库到一个单独的储存库中
在 JDK 9 中,有 8 个仓库: root、corba、hotspot、jaxp、jaxws、jdk、langtools 和 nashorn 。在 JDK 10 中这些将被合并为一个,使得跨相互依赖的变更集的存储库运行 atomic commit (原子提交)成为可能。
新 API
有 73 项新增内容添加到了标准类库中。
java.awt.Toolkitint getMenuShortcutKeyMaskEx():
确定哪个扩展修饰符键是菜单快捷键的适当加速键。
将此 Path2D 实例的容量计算到它当前的大小。应用可使用此操作将路径的存储空间最小化。这个方法也被添加到 Path2D.Double 和 Path2D.Float 类。
java.io.ByteArrayOutputStream:String toString(Charset):
重载 toString(),通过使用指定的字符集解码字节,将缓冲区的内容转换为字符串。
java.io.PrintStream:lang.io.PrintWriter:
这两个类都有三个新的构造函数,它们需要额外的 Charset 参数。
java.io.Reader:long transferTo(Writer):
从这个 Reader 中读取所有字符,并按照所读的顺序将字符写入给定的 Writer 。
java.lang.Runtime.Version:
有四种新方法返回新(JEP 322)版本字符串字段的整数值: feature()、interim()、patch() 和 update() 。
java.lang.StackWalker.StackFrame:String getDescriptor():
按照 JVM 标准返回此堆栈帧所代表的方法的描述符。
String getMethodType():
返回此堆栈帧所代表的方法类型,描述参数类型和返回值类型。
返回这个方法类型的最后一个参数类型。如果这个方法类型没有参数,则返回空类型作为岗哨值(Sentinel Value)。
R 返回正在运行的 JVM 的进程 ID 。
返回所有活动线程的线程信息,其中有指定的最大元素数量和同步信息的堆栈跟踪。
ThreadInfo[] getThreadInfo(long[], boolean, boolean, int):
返回每个线程的线程信息,这些线程的标识位于输入数组中,其中有指定的最大元素数量和同步信息的堆栈跟踪。
添加了一个新的构造函数,它以字符串的形式作为参数来获取详细信息。
java.net.URLDecoder:java.net.URLEncoder:
这两个类都有新的重载的解码和编码方法,将 charset 作为附加参数。
两个新的静态重载方法,允许使用 Charset 的 newReader(ReadByteChannel,Charset)和newWriter(WriteByteChannel,Charset)。
java.nio.file.FileStore:long getBlockSize():
在这个文件存储中返回每个块的字数。
这个包里有三个类,HijrahEra、MiinguoEra 和 ThaiBuddhistEra ,都有同样的方法。
String getDisplayName(TextStyle, Locale):
这将返回用于识别 era 的文本名称,适合于向用户展示。
返回指定格式器的一个副本,其中包含地区、日历、区域、小数和/或时区的本地化值,这将取代该格式器中的值。
java.util:
DoubleSummaryStatistics、IntSummaryStatistics 和 LongSummaryStatistics 都有一个新的构造函数,它包含 4 个数值。它使用指定的计数、最小值、最大值和总和构造一个非空实例。
java.util.List:java.util.Map:java.util.Set:
这些接口中的每一个都增加了一个新的静态方法,copyOf(Collection)。这些函数按照其迭代顺序返回一个不可修改的列表、映射或包含给定集合的元素的集合。
java.util.Optional:java.util.OptionalDouble:java.util.OptionalInt:java.util.OptionalLong:
每一个类都有一个新的方法,orElseThrow() ,它本质上和 get() 一样,也就是说,如果 Optional 有值则返回。否则,将抛出 NoSuchElementException 。
java.util.Formatter:java.util.Scanner:
这两个类都有三个新的构造函数,除了其他参数之外,它们都带有一个 charset 参数。
java.util.Properties:
这有一个新的构造函数,它接受一个 int 参数。这将创建一个没有默认值的空属性列表,并且指定初始大小以容纳指定的元素数量,而无需动态调整大小。还有一个新的重载的 replace 方法,接受三个 Object 参数并返回一个布尔值。只有在当前映射到指定值时,才会替换指定键的条目。
java.SplittableRandom:void nextBytes(byte[]):
用生成的伪随机字节填充一个用户提供的字节数组。
添加了 toString() 方法,该方法返回一个标识 FutureTask 的字符串,以及它的完成状态。在括号中,状态包含如下字符串中的一个,“Completed Normally” 、“Completed Exceptionally”、 “Cancelled” 或者 “Not completed”。
返回一个标记戳表示是否持有一个锁。
boolean isOptimisticReadStamp(long):
返回一个标记戳代表是否成功的进行了乐观读(optimistic read)。
boolean isReadLockStamp(long):
返回一个标记戳表示是否持有一个非独占锁(即 read lock )。
boolean isWriteLockStamp(long):
返回一个标记戳表示是否持有一个独占锁(即 write lock )。
java.jar.JarEntry:String getRealName():
返回这个 JarEntry 的真实名称。如果这个 JarEntry 是一个多版本 jar 文件的入口,它被配置为这样处理,这个方法返回的名字是 JarEntry 所代表的版本条目的入口,而不是 ZipEntry.getName() 返回的基本条目的路径名。如果 JarEntry 不代表一个多版本 jar 文件的版本化条目或者 jar 文件没有被配置为作为一个多版本 jar 文件进行处理,这个方法将返回与 ZipEntry.getName() 返回的相同名称。
返回 jar 文件中指定版本的入口对应 Stream 。与 JarEntry 的 getRealName 方法类似,这与多版本 jar 文件有关。
为给定的 Unicode 扩展键返回一个本地化名称。
getDisplayUnicodeExtensionType(String, String, Locale):
为给定的 Unicode 扩展键返回一个本地化名称。
这四个新方法都返回 Collectors ,将输入元素聚集到适当的不可修改的集合中。
现在有了一个字段,它代表了 JDK 10 的版本。
(我必须承认,我从来没听说过这些类)
R visitNoTypeAsModule(NoType, P):
访问一个 MODULE 的 pseudo-type 。我不确定为什么只有这两个类得到这个方法,因为还有 Visitor7 和 Visitor8 变量。
这个类已经添加了两个字段: CREDENTIALS_FILTER_PATTERN 和 SERIAL_FILTER_PATTERN 。
javax.ButtonModel:
看,Swing 还在更新!
ButtonGroup getGroup():
返回按钮所属的组。通常用于单选按钮,它们在组中是互斥的。
返回指定组件适合观感的最小大小。
JVM 规范改动
这些改动相当小:
4.6 节:类文件格式(第 99 页),在方法访问标志方面有小的改动。
4.7 节:模块属性(第 169 页),如果模块不是 java.base ,则 JDK 10 不再允许设置 ACC_TRANSITIVE 或 ACC_STATIC_PHASE 。
4.10 节:类文件的校验(第 252 页),dup2 指令已改变了 typesafe form 1 的定义,颠倒了 canSafleyPushList 一节中类型的顺序(你需要仔细查看才能发现它)。
5.2 节:Java 虚拟机启动(第 350 页),该描述添加了在创建初始类或接口时可使用用户定义的类加载器( bootstrap 类加载器除外)。
这里还有一些更改,但主要是为了支持局部变量类型推断:
3.8 节:标识符(第 23 页),在忽略了可忽略的字符之后,标识符的等价性现在被考虑了,这似乎是合乎逻辑的。
(第 24 页)一个新的 Token,TypeIdentifier,它支持对局部变量类型推断的新用法,而 var 的使用不是关键字,而是一个具有特殊含义的标识符,作为局部变量声明的类型。
4.10.5 节:类型预测(第 76 页),这是一个相当复杂的部分,它涉及到捕获变量、嵌套类以及如何使用局部变量类型推断。我建议你阅读规范中的这一部分,而不是试图解释它。
6.1 节:声明(第 134 页),一个反映使用 TypeIdentifier 来支持局部变量类型的推断的小改动。
6.5 节:确定名字的含义(第 153 页,第 158 页和第 159 页),根据类型标识符的使用而更改类类型。
6.5.4.1 节:简单的 PackageOrTypeNames(第 160 页)。
6.5.4.2节:合规的 PackageOrTypeNames(第 160 页),这两种方式都与使用 TypeIdentifier 有细微的变化。
7.5.3 节:单静态导入声明(第 191 页),这改变了导入具有相同名称的静态类型的规则。除非类型是相同的,否则这将成为一个错误,在这种情况下,重复被忽略。
7.7.1 节:依赖(第 198 页),如果你明确声明一个模块需要 java.base ,那在必要的关键字之后,你就不能再使用修饰符(例如静态)了。
第 8 部分:正式参数(第 244 页),接收者参数可能只出现在一个实例方法的 formalparameters 列表,或者是一个内部类的构造函数中,其中内部类没有在静态上下文中声明。
9.7.4 节:注释可能出现的地方(第 335 页),有一个与局部变量类型推断相关的变更。
14.4 部分:局部变量声明语句(第 433 页),实现局部变量类型推断所需的大量更改。
14 节:增强的 for 语句(第 455 页),这个结构已经更新,包括对局部变量类型推断的支持。
14.20.3 节:try-with-resources(474 页),这个结构已经更新,包括对局部变量类型推断的支持。
最后,第 19 章有多处语法更新,反映了应更多使用 TypeIdentifier 类型标识符,而不仅仅是 Identifier 标识符,以支持局部变量类型推断。
其他更新内容
如果 Kerberos 的配置文件 krb5.conf 包含一个 INCLUDEDIR 选项,那么在 INCLUDEDIR 这个目录下所有以 .conf 结尾的文件都会被默认加载进来。
以前版本中已经过期的 Java 的启动选项 -d32 和 –d64 在当前版本已经被移除。如果你在新的版本里仍然使用了这两个选项,JVM 将无法正常启动。
JDK 10 支持 JDK 9 中的新版本 Doclet,JDK 6、JDK 7、JDK 8 中的 Doclet 版本都不再支持。
JDK 10 重新启用了在 JDK 9 中被不当过时的 newFactory() 方法。
JDK 10 引入了一个新的 Javadoc 标签: {@summary…},解决了以前版本无法生成 API 摘要的问题。
JDK 10 去掉了 BiasedLockingStartupDelay 的 4 秒启动延时。
PolicyFile
SolarisNumericGroupPrincipal
SolarisNumericUserPrincipal
X500Principal
SolarisLoginModule
SolarisSystem
在 java.lang.SecurityManager 类中的以下属性和方法(从 JDK 1.2 就已经过时)终于被移除了:
inCheck (属性)
getInCheck
classDepth
classLoaderDepth
currentClassLoader
currentLoadedClass
inClass
inClassLoader
以下 java.lang.Runtime 类中已经被废弃的国际化方法在新版本被移除:
getLocalizedInputStream
getLocalizedOutputStream
以下废弃的 Hotspot –X 选项在新版本中被移除:
-Xoss
-Xsqnopause
Xoptimize
-Xboundthreads
–Xusealtsigs
policytool 在新版本中被移除。
javadoc 工具在新版本中可以通过 –add-stylesheets 命令选项支持多个 stylesheets 。
新版本的 JVM 能够根据系统分配给当前 Docker 容器的 CPU 数和内存来配置线程池和 GC 机制,而不再是直接使用系统的 CPU 和内存。并且增加了三个更强大的命令选项:
-XX:InitialRAMPercentage
-XX:MaxRAMPercentage
-XX:MinRAMPercentage
新版本增加了一个新的系统属性:jdk.disableLastUsageTracking。这个新增的属性就像它的名字一样,会禁用 JRE 的上一次使用跟踪。
如上所述,尽管距离 JDK 9 发布仅六个月的时间,但 JDK 10 实际上有相当多的变化。当然,它们中的一些是非常小的变更,但我认为这表明目前每 6 个月发布一次的节奏,将在 Java 平台快速迭代改进方面起到作用,同时也让我们期待 JDK 11 将带来的内容。
领取专属 10元无门槛券
私享最新 技术干货