Java

Java10新特性预览

jdk1.10正式版将在2018年3月份发布,jdk1.10变化内容:
1.局部变量类型推断:局部变量类型推断将引入”var”关键字,也就是可以随意定义变量而不必指定变量的类型.
2.GC改进和内存管理
3.线程本地握手(JEP 312)
4.备用内存设备上的堆分配(JEP 316)
5.其他Unicode语言 - 标记扩展(JEP 314)
6.基于Java的实验性JIT编译器
7.根证书(JEP 319)
9.将JDK生态整合单个存储库(JEP 296)
10.删除工具javah(JEP 313)


  • 局部变量类型推断,举个例子,var list = new ArrayList();,可以使用 var 来作为变量类型,Java 编译器知道 list 的类型为字符串的 ArrayList。
  • 增强 java.util.Locale
  • 提供了一组默认的根证书颁发机构(CA)。

Java10新特性 - 图1

1、局部变量类型推断

JDK 10增加了局部变量类型推断(Local-Variable Type Inference)功能,让 Java 可以像Js里的var一样可以自动推断数据类型。Java中的var是一个保留类型名称,而不是关键字。
JDK 10之前

  1. List<String> list = new ArrayList<String>();
  2. Stream<Integer> stream = Stream.of(1, 2, 3);

JDK 10 之后

  1. var list = new ArrayList<String>(); // ArrayList<String>
  2. var stream = Stream.of(1, 2, 3);

var 变量类型推断的使用也有局限性,仅「局限」于以下场景:

  • 具有初始化器的局部变量
  • 增强型for循环中的索引变量
  • 传统for循环中声明的局部变量

而「不能用于」

  • 推断方法的参数类型
  • 构造函数参数类型推断
  • 推断方法返回类型
  • 字段类型推断
  • 捕获表达式

Scala 和 Kotlin 中有 val 关键字 ( final var 组合关键字),Java10 中并没有引入。
Java 10 只引入了 var,而

  1. var id = 0;
  2. var codefx = new URL("https://mp.weixin.qq.com/");
  3. var list = new ArrayList<>();
  4. var list = List.of(1, 2, 3);
  5. var map = new HashMap<String, String>();
  6. var p = Paths.of("src/test/java/Java9FeaturesTest.java");
  7. var numbers = List.of("a", "b", "c");
  8. for (var n : list)
  9. System.out.print(n+ " ");

var 关键字只能用于带有构造器的局部变量和 for 循环中。

  1. var count=null; //❌编译不通过,不能声明为 null
  2. var r = () -> Math.random();//❌编译不通过,不能声明为 Lambda表达式
  3. var array = {1,2,3};//❌编译不通过,不能声明数组

var 并不会改变 Java 是一门静态类型语言的事实,编译器负责推断出类型。

var 不应该被滥用

虽然这样“爽起来了”,但是var也不应该被滥用。
下面这种写法明细可读性差,导致变量的类型需要你去DEBUG:

  1. var data = someObject.getData();

Stream流中也尽量不要使用:

  1. // 可读性差
  2. var names= apples.stream()
  3. .map(Apple::getName)
  4. .collect(Collectors.toList());

因此,在使用var时应该保证必要的可读性。
另外,在多态这个重要的Java特性中,var表现的并不是很完美。如果FruitAppleOrange两种实现。

  1. var x = new Apple();

如果对x重新赋值为new Orange()就会报错,因为编译后x的类型就已经固定下来了。所以var和泛型一样都是在编译过程中起了作用。必须保证var的类型是确定的。
var结合泛型的钻石符号<>会有什么情况发生呢?
下面的 empList的类型是ArrayList<Object>

  1. var empList = new ArrayList<>();

如果需要明确集合中放的都是Apple就必须在右边显式声明:

  1. var apples = new ArrayList<Apple>();

2、不可变集合的改进

JDK 10中,List,Set,Map 提供了一个新的静态方法copyOf(Collection<? extends E> coll),它返回Collection集合一个不可修改的副本。
JDK 源码:

  1. static <E> List<E> copyOf(Collection<? extends E> coll) {
  2. return ImmutableCollections.listCopy(coll);
  3. }

使用实例:

  1. var oldList = new ArrayList<String>();
  2. oldList.add("Hello");
  3. oldList.add("Fcant");
  4. var copyList = List.copyOf(oldList);
  5. oldList.add("World");
  6. copyList.add("Fcscanf"); //UnsupportedOperationException异常

使用 copyOf() 创建的集合为不可变集合,不能进行添加、删除、替换、 排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。IDEA 也会有相应的提示。
2021-09-06-16-57-05-467867.png
java.util.stream.Collectors 中新增了静态方法,用于将流中的元素收集为不可变的集合。

  1. var list = new ArrayList<>();
  2. list.stream().collect(Collectors.toUnmodifiableList());
  3. list.stream().collect(Collectors.toUnmodifiableSet());
  • 不可变性(immutability),这是函数式编程的基石之一,因此加强不可变集合有助于函数式编程在Java中的发展。
  • 安全性,由于集合不可变,因此就不存在竞态条件,天然的线程安全性,无论在编码过程中和内存使用中都有一定的优势,这种特性在Scala和Kotlin这两种编程语言中大放异彩。

在Java 10 中又引入了一些新的API。

集合副本

复制一个集合为不可变集合:

  1. List<Apple> copyList = List.copyOf(apples);

任何修改此类集合的尝试都会导致java.lang.UnsupportedOperationException异常。

Stream归纳为不可变集合

之前Stream API的归纳操作collect(Collector collector)都只会把流归纳为可变集合,现在它们都有对应的不可变集合了。举个例子:

  1. List<String> names= apples.stream() .map(Apple::getName) .collect(Collectors.toUnmodifiableList());

3、并行全垃圾回收器 G1

JDK 9引入 G1 作为默认垃圾收集器,执行GC 时采用的是基于单线程标记扫描压缩算法(mark-sweep-compact)。为了最大限度地减少 Full GC 造成的应用停顿的影响,Java 10 中将为 G1 引入多线程并行 GC,同时会使用与年轻代回收和混合回收相同的并行工作线程数量,从而减少了 Full GC 的发生,以带来更好的性能提升、更大的吞吐量。

从 Java9 开始 G1 就了默认的垃圾回收器,G1 是以一种低延时的垃圾回收器来设计的,旨在避免进行 Full GC,但是 Java9 的 G1 的 FullGC 依然是使用单线程去完成标记清除算法,这可能会导致垃圾回收期在无法回收内存的时候触发 Full GC。
为了最大限度地减少 Full GC 造成的应用停顿的影响,从 Java10 开始,G1 的 FullGC 改为并行的标记清除算法,同时会使用与年轻代回收和混合回收相同的并行工作线程数量,从而减少了 Full GC 的发生,以带来更好的性能提升、更大的吞吐量。

4、线程本地握手

Java 10 中线程管控引入JVM安全点的概念,将允许在不运行全局JVM安全点的情况下实现线程回调,由线程本身或者JVM线程来执行,同时保持线程处于阻塞状态,这将会很方便使得停止单个线程或不停止线程成为可能。

5、Optional新增orElseThrow()方法

OptionalOptionalDouble等类新增一个方法orElseThrow(),在没有值时抛出异常

  1. Optional.ofNullable(cache.getIfPresent(key))
  2. .orElseThrow(() -> new PrestoException(NOT_FOUND, "Missing entry found for key: " + key));

Optional如果值为null时去get会抛出NoSuchElementException异常。从语义上get应该肯定能得到什么东西,但是实际上异常了,这种歧义性太大了。所以增加了一个orElseThrow()方法来增强语义性。

  1. Optional<String> optional = Optional.ofNullable(nullableVal);
  2. // 可能会 NoSuchElementException String nullable = optional.get();

6、应用程序类数据共享(扩展 CDS 功能)

在 Java 5 中就已经引入了类数据共享机制 (Class Data Sharing,简称 CDS),允许将一组类预处理为共享归档文件,以便在运行时能够进行内存映射以减少 Java 程序的启动时间,当多个 Java 虚拟机(JVM)共享相同的归档文件时,还可以减少动态内存的占用量,同时减少多个虚拟机在同一个物理或虚拟的机器上运行时的资源占用。CDS 在当时还是 Oracle JDK 的商业特性。
Java 10 在现有的 CDS 功能基础上再次拓展,以允许应用类放置在共享存档中。CDS 特性在原来的 bootstrap 类基础之上,扩展加入了应用类的 CDS 为 (Application Class-Data Sharing,AppCDS) 支持,大大加大了 CDS 的适用范围。其原理为:在启动时记录加载类的过程,写入到文本文件中,再次启动时直接读取此启动文本并加载。设想如果应用环境没有大的变化,启动速度就会得到提升。

7、Java10 其他新特性

  • 线程-局部管控:Java 10 中线程管控引入 JVM 安全点的概念,将允许在不运行全局 JVM 安全点的情况下实现线程回调,由线程本身或者 JVM 线程来执行,同时保持线程处于阻塞状态,这种方式使得停止单个线程变成可能,而不是只能启用或停止所有线程
  • 备用存储装置上的堆分配:Java 10 中将使得 JVM 能够使用适用于不同类型的存储机制的堆,在可选内存设备上进行堆内存分配
  • 统一的垃圾回收接口:Java 10 中,hotspot/gc 代码实现方面,引入一个干净的 GC 接口,改进不同 GC 源代码的隔离性,多个 GC 之间共享的实现细节代码应该存在于辅助类中。统一垃圾回收接口的主要原因是:让垃圾回收器(GC)这部分代码更加整洁,便于新人上手开发,便于后续排查相关问题。
  • 基于 Java 的 实验性 JIT 编译器
  • 类数据共享
  • Unicode 语言标签扩展
  • 根证书
  • 基于时间(Time-Based)的版本控制模型