Java10介绍
JDK10是JavaSE10的一个实现,于2018年3月20日发布。
在本文中,我们将介绍和探讨JDK10中引入的新特性和更改。
1. 局部变量类型推断
在Java 9之前,我们必须明确提到局部变量的类型,并确保它与用于初始化它的初始值设定项兼容:
String message = "Good bye, Java 9";
在Java 10中,我们可以这样声明局部变量:
@Test
public void whenVarInitWithString_thenGetStringTypeVar() {
var message = "Hello, Java 10";
assertTrue(message instanceof String);
}
我们不提供消息的数据类型。相反,我们将消息标记为var
,编译器根据右侧的初始值设定项的类型推断消息的类型。
在上面的示例中,消息的类型将是String
。
请注意,此功能仅适用于具有初始值设定项的局部变量。它不能用于成员变量、方法参数、返回类型等–需要初始值设定项,否则编译器将无法推断类型。
此增强有助于减少样板代码;例如:
Map<Integer, String> map = new HashMap<>();
现在可以重写为:
var idToNameMap = new HashMap<Integer, String>();
这也有助于关注变量名而不是变量类型。
另一件需要注意的事情是var不是一个关键字——这确保了使用var的程序的向后兼容性,比如说,作为函数或变量名。var
是一个保留类型名,就像int
一样。
最后,请注意,使用var
没有运行时开销,也没有使Java成为动态类型化语言。变量的类型仍在编译时推断,以后无法更改。
1.1. 非法使用var
如前所述,没有初始值设定项,var
将无法工作:
var n; // error: cannot use 'var' on variable without initializer
如果初始化为null也不起作用:
var emptyList = null; // error: variable initializer is 'null'
它不适用于非局部变量:
public var = "hello"; // error: 'var' is not allowed here
Lambda表达式需要显式目标类型,因此不能使用var:
var p = (String s) -> s.length() > 10; // error: lambda expression needs an explicit target-type
数组初始值设定项的情况也是如此:
var arr = { 1, 2, 3 }; // error: array initializer needs an explicit target-type
1.2. var使用指南
有些情况下,var
可以合法使用,但这样做可能不是一个好主意。
例如,在代码可能变得不太可读的情况下:
var result = obj.prcoess();
在这里,虽然var
是合法的用法,但是很难理解process()
返回的类型,从而降低代码的可读性。
另一种最好避免var
的情况是在管道较长的流中:
var x = emp.getProjects.stream()
.findFirst()
.map(String::length)
.orElse(0);
var
的使用也可能产生意想不到的结果。
例如,如果我们将其与Java 7中引入的diamond
运算符一起使用:
var empList = new ArrayList<>();
emplist
的类型将是ArrayList<Object>
而不是List<Object>
。如果我们希望它是ArrayList<Employee>
,我们必须明确:
var empList = new ArrayList<Employee>();
对非可表示类型使用var
可能会导致意外错误。
例如,如果我们对匿名类实例使用var
:
@Test
public void whenVarInitWithAnonymous_thenGetAnonymousType() {
var obj = new Object() {};
assertFalse(obj.getClass().equals(Object.class));
}
现在,如果我们尝试将另一个object分配给obj,我们将得到一个编译错误:
obj = new Object(); // error: Object cannot be converted to <anonymous Object>
这是因为obj的推断类型不是Object。
2. 不可修改的集合
Java10中有几个与不可修改集合相关的更改。
2.1. copyOf()方法
java.util.List, java.util.Map
文件以及java.util.Set
每个都有一个新的静态方法copyOf(Collection)
。
它返回给定集合的不可修改副本:
@Test(expected = UnsupportedOperationException.class)
public void whenModifyCopyOfList_thenThrowsException() {
List<Integer> copyList = List.copyOf(someIntList);
copyList.add(4);
}
任何修改此类集合的尝试都将导致java.lang.UnsupportedOperationException运行时例外。
2.2. toUnmodifiable*()
java.util.stream.Collectors
获取其他方法以将流收集到不可修改的列表、映射或集合中:
@Test(expected = UnsupportedOperationException.class)
public void whenModifyToUnmodifiableList_thenThrowsException() {
List<Integer> evenList = someIntList.stream()
.filter(i -> i % 2 == 0)
.collect(Collectors.toUnmodifiableList());
evenList.add(4);
}
3. Optional*.orElseThrow()
java.util.Optional,java.util.OptionalDouble,java.util.OptionalIntand,java.util.OptionalLongeach
选项获取了一个新方法orelsethorn()
,该方法不接受任何参数,如果不存在值,则抛出nosuchelementexception:
@Test
public void whenListContainsInteger_OrElseThrowReturnsInteger() {
Integer firstEven = someIntList.stream()
.filter(i -> i % 2 == 0)
.findFirst()
.orElseThrow();
is(firstEven).equals(Integer.valueOf(2));
}
它是现有get()
方法的同义词,现在是现有get()
方法的首选替代方法。
4. 性能改进
这些改进适用于在JDK10下运行的所有应用程序,不需要任何代码更改就可以利用它们。
4.1. Parallel Full GC for G1
G1垃圾收集器是自jdk9以来的默认垃圾收集器。但是,G1的完整GC使用了单线程标记扫描压缩算法。
在java10中,这被改为并行标记扫描压缩算法,有效地减少了在完全GC期间的停止世界时间。
4.2. 应用程序类数据共享
JDK5中引入的类数据共享允许将一组类预处理到一个共享的归档文件中,然后在运行时对其进行内存映射,以减少启动时间,这也可以减少多个JVM共享同一归档文件时的动态内存占用。
CDS只允许引导类加载器,将此功能限制为只支持系统类。应用程序cd(appcd)扩展了cd,允许内置系统类加载器(又称“app类加载器”)、内置平台类加载器和自定义类加载器加载存档的类。这使得对应用程序类使用该特性成为可能。
我们可以使用以下步骤来使用此功能:
1. 获取要存档的类的列表
下面的命令将把HelloWorld
应用程序加载的类转储到hello.lst
:
$ java -Xshare:off -XX:+UseAppCDS -XX:DumpLoadedClassList=hello.lst \
-cp hello.jar HelloWorld
2. 创建AppCD存档
以下命令创建hello.js使用hello.lst作为输入:
$ java -Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=hello.lst \
-XX:SharedArchiveFile=hello.jsa -cp hello.jar
3. 使用AppCD存档
以下命令启动HelloWorld应用程序你好,jsa作为输入:
$ java -Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=hello.jsa \
-cp hello.jar HelloWorld
AppCDS是oracle jdk for jdk8和jdk9的商业特性。现在它是开源的,并公开提供。
4. 基于Java的JIT编译器实验
Graal是一个用Java编写的动态编译器,它与HotSpot JVM集成;它专注于高性能和可扩展性。它也是JDK9中引入的实验性提前(AOT)编译器的基础。
jdk10支持Graal编译器,在Linux/x64平台上用作实验性的JIT编译器。
要启用Graal作为JIT编译器,请在java命令行上使用以下选项:
-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler
请注意,这是一个实验性的特性,我们不一定能获得比现有JIT编译器更好的性能。
5.容器意识
JVM现在意识到在Docker容器中运行,并将提取特定于容器的配置,而不是查询操作系统本身—它适用于分配给容器的CPU数量和总内存等数据。
但是,此支持仅适用于基于Linux的平台。默认情况下会启用此新支持,并且可以在命令行中使用JVM选项禁用此支持:
-XX:-UseContainerSupport
此外,此更改还添加了一个JVM选项,该选项提供了指定JVM将使用的CPU数量的功能:
-XX:ActiveProcessorCount=count
此外,还添加了三个新的JVM选项,以允许Docker容器用户对将用于Java堆的系统内存量进行更细粒度的控制:
-XX:InitialRAMPercentage
-XX:MaxRAMPercentage
-XX:MinRAMPercentage
6. 根证书
cacerts密钥库最初是空的,目的是包含一组根证书,用于在各种安全协议使用的证书链中建立信任。
因此,在OpenJDK构建下,关键的安全组件(如TLS)在默认情况下不起作用。
通过Java10,Oracle在Oracle的JavaSE根CA程序中公开了根证书的来源,以使OpenJDK构建对开发人员更有吸引力,并减少这些构建与Oracle JDK构建之间的差异。
7. Deprecations and Removals
7.1. 命令行选项和工具
工具javah
已经从java10中删除了,java10生成了实现本机方法所需的C头文件和源文件,现在可以改用javac-h
。
policytool
是用于策略文件创建和管理的基于UI的工具。现在已删除。用户可以使用简单的文本编辑器来执行此操作。
删除了java-xprof
选项。该选项用于分析正在运行的程序并将分析数据发送到标准输出。用户现在应该改用jmap
工具。
7.2. APIs
已弃用java.security.acl
文件包已标记为forremovation=true
,并且可能在将来的Java SE版本中被删除。它被替换为java.security.Policy
策略以及相关的课程。
同样地,java.security.{Certificate,Identity,IdentityScope,Signer}
api被标记为remove=true
。
8. 基于时间的版本控制
从Java10开始,Oracle转向了基于时间的Java版本。这有以下含义:
1. 每六个月发布一次新的Java版本。2018年3月发布的是JDK10,2018年9月发布的是JDK11,以此类推。这些被称为特性发布,预计至少包含一个或两个重要特性
2. 对该特性版本的支持将只持续六个月,即直到下一个特性版本
3. 长期支持释放将标记为LTS。对这种释放的支持将持续三年
4. Java11将是一个LTS版本
自由星
java版本现在将包含GA日期,从而更容易确定版本的发布时间:
$ java -version
openjdk version "10" 2018-03-20
OpenJDK Runtime Environment 18.3 (build 10+46)
OpenJDK 64-Bit Server VM 18.3 (build 10+46, mixed mode)
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/1414.html
暂无评论