4年前 (2021-01-30)  jvm |   抢沙发  878 
文章评分 0 次,平均分 0.0

Java10的新特性

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

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册