1 JDK11新特性介绍

1.1 初识JDK11新特性

  • 北京时间2018年9月26日,Oracle官方宣布JDK11(18.9LTS)正式发布。这是Java8以后支持的首个长期版本,非常值得关注。最近发布的JDK11将带来ZGC、HttpClient等重要特性,一共包含了17个JEP(Java Enhancement Proposals,Java增强提案)。
  • 本次发布的JDK11和2017年9月份发布的JDK9以及2018年发布的JDK11相比,最大的区别就是:在长期支持方面,Oracle表示会对JDK11提供大力支持。

1.2 JDK11新特性更新列表

  • 我们可以在如下网站中查看JDK11的新特性增强提案详情:
  1. http://openjdk.java.net/projects/jdk/11/

JDK11新特性特性列表.png

2 JDK11新特性详解

2.1 基于嵌套的访问控制

  • 如果在一个类中嵌套了多个类,各个类可以直接访问彼此的私有成员。JDK11在private、public、protected的基础上,JVM又提供了新的访问机制:Nest。
  • JDK11之前的如下案例:
  1. package com.sunxiaping;
  2. /**
  3. * @author 许大仙
  4. * @version 1.0
  5. * @since 2020-12-03 10:33
  6. */
  7. public class Outer {
  8. private String str ;
  9. public class Inner{
  10. public void method(){
  11. System.out.println("str = " + str);
  12. }
  13. }
  14. }
  • 在JDK11之前,执行编译的最终结果的class文件形式如下:
  1. public class Outer {
  2. private String str;
  3. public String access$000(){
  4. return str;
  5. }
  6. }
  7. public class Inner$Outer {
  8. public void method() {
  9. System.out.println("str = " + Outer.access$000(Outer.this));
  10. }
  11. }
  • 以上方案虽然从逻辑上讲,内部类和外部类相同代码实体的一部分,但它被编译为一个单独的类,因此,它需要编译器创建合成桥接方法,以提供对外部类的私有字段的访问。
  • 这种方案一个很大的坑是反射的时候有问题,当使用反射在内部类中访问外部类私有成员str时会报IllegalAccessException异常,这个是让人难以理解的,因为反射还是源码级访问的权限。
  1. package com.sunxiaping;
  2. import java.lang.reflect.Field;
  3. /**
  4. * @author 许大仙
  5. * @version 1.0
  6. * @since 2020-12-03 10:33
  7. */
  8. public class Outer {
  9. private String outInt;
  10. public class Inner {
  11. public void method() {
  12. try {
  13. Class outerClass = Outer.class;
  14. Outer outer = Outer.class.getDeclaredConstructor().newInstance();
  15. Field field = outerClass.getDeclaredField("outInt");
  16. field.set(outer, "100");
  17. System.out.println("str = " + outer.outInt);
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. }
  23. }
  1. package com.sunxiaping;
  2. /**
  3. * 基于嵌套的访问控制
  4. *
  5. * @author 许大仙
  6. * @version 1.0
  7. * @since 2020-12-03 09:06
  8. */
  9. public class Demo {
  10. public static void main(String[] args) {
  11. new Outer().new Inner().method();
  12. }
  13. }
  • JDK11开始,嵌套时一种访问控制上下文,它运行多个class同属一个逻辑代码块,但是被编译成多个分散的class文件,它们访问彼此的私有成员无需通过编译器添加访问扩展犯法,而是可以直接访问,如果上述代码可以直接通过反射访问外部类的私有成员,而不会出现权限问题。

2.2 Lambda参数局部变量

  • 在声明隐式类型的Lambda表示式形参的时候允许使用var,这样使用的好处在于使用Lambda表示式时可以给参数加上注解。

  • 示例:

  1. package com.sunxiaping;
  2. import java.util.List;
  3. /**
  4. * @author 许大仙
  5. * @version 1.0
  6. * @since 2020-12-03 11:27
  7. */
  8. public class LambdaDemo {
  9. public static void main(String[] args) {
  10. List<Integer> list = List.of(1, 2, 3, 4, 5, 6, 7, 8);
  11. list.stream().sorted((@Deprecated var a,@Deprecated var b) -> a - b).forEach(System.out::println);
  12. }
  13. }

2.3 String新增处理方法

  • 判断字符串是否为空白:
  1. //如果字符串都为空格,制表符、tab的情况,返回true,否则返回false
  2. public boolean isBlank();
  • 判断字符串是否为空:
  1. //判断字符串内容是否为空,不忽略空格等
  2. public boolean isEmpty()
  • 去除首尾空白:
  1. //可以去除全角的空白字符
  2. public String strip()
  • 去除首尾空白:
  1. //不能去除全角的空白字符
  2. public String trim()
  • 去除尾部空白:
  1. public String stripTrailing()
  • 去除首部空白:
  1. public String stripLeading()
  • 复制字符串:
  1. public String repeat(int count)
  • 行数统计:
  1. public Stream<String> lines()
  • 示例:判断字符串是否为空白和判断字符串是否为空
  1. package com.sunxiaping;
  2. /**
  3. * @author 许大仙
  4. * @version 1.0
  5. * @since 2020-12-03 13:25
  6. */
  7. public class StringDemo {
  8. public static void main(String[] args) {
  9. String str = "";
  10. System.out.println(str.isBlank()); //true
  11. System.out.println(str.isEmpty()); //true
  12. str = " ";
  13. System.out.println(str.isBlank()); //true
  14. System.out.println(str.isEmpty()); //false
  15. str = "abc";
  16. System.out.println(str.isBlank()); //false
  17. System.out.println(str.isEmpty()); //false
  18. }
  19. }
  • 示例:去除首尾空白
  1. package com.sunxiaping;
  2. /**
  3. * @author 许大仙
  4. * @version 1.0
  5. * @since 2020-12-03 13:25
  6. */
  7. public class StringDemo {
  8. public static void main(String[] args) {
  9. String str = " 哈哈 ";
  10. System.out.println(str.strip());
  11. System.out.println(str.trim());
  12. }
  13. }
  • 示例:去除首部空白和尾部空白
  1. package com.sunxiaping;
  2. /**
  3. * @author 许大仙
  4. * @version 1.0
  5. * @since 2020-12-03 13:25
  6. */
  7. public class StringDemo {
  8. public static void main(String[] args) {
  9. String str = " 哈哈 ";
  10. System.out.println(str.stripTrailing()); //去除尾部空白
  11. System.out.println(str.stripLeading()); //去除首部空白
  12. }
  13. }
  • 示例:复制字符串
  1. package com.sunxiaping;
  2. /**
  3. * @author 许大仙
  4. * @version 1.0
  5. * @since 2020-12-03 13:25
  6. */
  7. public class StringDemo {
  8. public static void main(String[] args) {
  9. String str = "我是中国人,我爱中国。";
  10. //复制字符串
  11. System.out.println(str.repeat(3)); //我是中国人,我爱中国。我是中国人,我爱中国。我是中国人,我爱中国。
  12. }
  13. }
  • 示例:行数统计
  1. package com.sunxiaping;
  2. /**
  3. * @author 许大仙
  4. * @version 1.0
  5. * @since 2020-12-03 13:25
  6. */
  7. public class StringDemo {
  8. public static void main(String[] args) {
  9. String str = "我\n是\n中\n国\n人\n,我\n爱\n中\n国。";
  10. System.out.println(str.lines().count()); //行数统计:9
  11. }
  12. }

2.4 集合新增的API

  • 集合转换数组支持方法引用。
  1. default <T> T[] toArray(IntFunction<T[]> generator)
  • 示例:
  1. package com.sunxiaping;
  2. import java.util.Arrays;
  3. import java.util.List;
  4. /**
  5. * @author 许大仙
  6. * @version 1.0
  7. * @since 2020-12-03 13:51
  8. */
  9. public class ListDemo {
  10. public static void main(String[] args) {
  11. List<Integer> list = List.of(1, 2, 3, 4, 5);
  12. //JDK11之前转换数组
  13. Integer[] array = list.toArray(new Integer[0]);
  14. Arrays.stream(array).forEach(System.out::println);
  15. //JDK11转换数组
  16. Integer[] array1 = list.toArray(Integer[]::new);
  17. Arrays.stream(array1).forEach(System.out::println);
  18. }
  19. }

2.5 更方便的操作IO

  • Path类获取指定目录的路径:
  1. //等同于Paths.get()方法
  2. public static Path of(String first, String... more)
  • Files更方便的读写数据:
  1. //写数据
  2. public static Path writeString(Path path, CharSequence csq, OpenOption... options)
  1. //读取数据
  2. public static String readString(Path path, Charset cs) throws IOException
  • 示例:
  1. package com.sunxiaping;
  2. import java.nio.charset.StandardCharsets;
  3. import java.nio.file.Files;
  4. import java.nio.file.Path;
  5. /**
  6. * @author 许大仙
  7. * @version 1.0
  8. * @since 2020-12-03 14:04
  9. */
  10. public class IODemo {
  11. public static void main(String[] args) throws Exception {
  12. //Files类更方便的读写数据方法
  13. Files.writeString(Path.of("demo.txt"),"在学习Java11");
  14. String str = Files.readString(Path.of("demo.txt"), StandardCharsets.UTF_8);
  15. System.out.println(str);
  16. }
  17. }

2.6 HttpClient

2.6.1 概述

  • 从Java9开始开始引入了一个处理HTTP请求的Http Client API,不过当时一直处于孵化阶段,而在Java11中已经成为正式可用状态,作为一个标准API提供在java.net.http供我们使用,该API支持同步和异步请求。
  • HttpClient目的是取代JDK8中的httpURLConnection。

2.6.2 优势

  • API是易于使用的,包括简单的阻塞模式。
  • 支持通知机制如HTTP消息头收到、错误码、HTTP消息体收到。
  • 简洁的API能够支持80~90%的需求。
  • 支持标准和通用身份验证机制。
  • 能够轻松使用WebSocket。
  • 支持HTTP2。
  • 执行和现有网络API一致的安全检查。
  • 对Lambda表达式等新语言功能友好。
  • 对嵌入式系统友好,避免永久运行的后台线程。
  • 支持HTTPs/TLS。
  • 满足HTTP1.1和HTTP2的性能要求。

2.6.3 应用示例

  • 示例:
  1. package com.sunxiaping;
  2. import org.junit.Test;
  3. import java.io.IOException;
  4. import java.net.URI;
  5. import java.net.http.HttpClient;
  6. import java.net.http.HttpRequest;
  7. import java.net.http.HttpResponse;
  8. import java.util.concurrent.CompletableFuture;
  9. /**
  10. * @author 许大仙
  11. * @version 1.0
  12. * @since 2020-12-03 14:31
  13. */
  14. public class HttpClientDemo {
  15. /**
  16. * 同步
  17. */
  18. @Test
  19. public void test() throws IOException, InterruptedException {
  20. //创建HttpClient对象
  21. HttpClient httpClient = HttpClient.newHttpClient();
  22. //创建HttpRequest请求对象
  23. HttpRequest httpRequest = HttpRequest.newBuilder(URI.create("https://www.baidu.com")).GET().build();
  24. //使用HttpClient对象发起request请求,得到请求响应对象response
  25. HttpResponse<String> response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
  26. //响应状态码
  27. System.out.println("响应状态码:" + response.statusCode());
  28. //响应信息
  29. System.out.println("响应信息:" + response.body());
  30. }
  31. /**
  32. * 异步
  33. */
  34. @Test
  35. public void test2() {
  36. //创建HttpClient对象
  37. HttpClient httpClient = HttpClient.newHttpClient();
  38. //创建HttpRequest请求对象
  39. HttpRequest httpRequest = HttpRequest.newBuilder(URI.create("https://www.baidu.com")).GET().build();
  40. //发送请求
  41. CompletableFuture<HttpResponse<String>> future = httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString());
  42. //异步监听结果数据
  43. future.whenComplete((response, throwable) -> {
  44. //结果数据的处理
  45. if (null != throwable) {
  46. throwable.printStackTrace();
  47. } else {
  48. //响应状态码
  49. System.out.println("响应状态码:" + response.statusCode());
  50. //响应信息
  51. System.out.println("响应信息:" + response.body());
  52. }
  53. }).join(); //阻塞等待异步结果
  54. System.out.println("结束程序");
  55. }
  56. }

2.7 改进Aarch64内在函数

  • 改进现有的字符串和数组函数,并在Aarch64处理器上为java.lang.Math下的sin、cos和log函数实现新的内联函数,从而实现专用的CPU架构下提高应用程序的性能。
  • 示例:
  1. package com.sunxiaping;
  2. import java.util.concurrent.TimeUnit;
  3. /**
  4. * @author 许大仙
  5. * @version 1.0
  6. * @since 2020-12-03 14:54
  7. */
  8. public class Aarch64Demo {
  9. public static void main(String[] args) {
  10. long startTime = System.nanoTime();
  11. for (int i = 0; i < 10000000; i++) {
  12. Math.sin(1);
  13. Math.cos(1);
  14. Math.log(1);
  15. }
  16. long endTime = System.nanoTime();
  17. System.out.println(TimeUnit.NANOSECONDS.toMillis(endTime - startTime));
  18. }
  19. }

2.8 增强java启动器支持运行单个java源程序文件的程序

  • 使用java命令可以编译和运行源文件程序。
  • 示例:
  1. package com.sunxiaping;
  2. /**
  3. * @author 许大仙
  4. * @version 1.0
  5. * @since 2020-12-03 15:59
  6. */
  7. public class HelloWorld {
  8. public static void main(String[] args) {
  9. System.out.println("Hello World");
  10. }
  11. }
  1. java HelloWorld.java

使用java命令可以编译和运行源文件程序.png

2.9 Epsilon垃圾收集器

2.9.1 概述

  • Epsilon垃圾收集器,又称为无操作垃圾收集器。
  • JDK对这个特性的描述是:开发一个处理内存分配但是不实现任何实际内存回收机制的GC,一旦Java的堆内存被耗尽,JVM就直接关闭。
  • 如果有system.gc();调用,实际上什么也不会发生垃圾对象的回收操作,因为没有内存回收,这个实现可能会警告用户尝试强制GC是徒劳的。

2.9.2 应用示例

  • 示例:使用G1垃圾收集器
  1. package com.sunxiaping;
  2. /**
  3. * @author 许大仙
  4. * @version 1.0
  5. * @since 2020-12-03 16:19
  6. */
  7. public class Garbage {
  8. private int number;
  9. public Garbage() {
  10. }
  11. public Garbage(int number) {
  12. this.number = number;
  13. }
  14. @Override
  15. protected void finalize() throws Throwable {
  16. System.out.println(number);
  17. }
  18. }
  1. package com.sunxiaping;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. /**
  5. * @author 许大仙
  6. * @version 1.0
  7. * @since 2020-12-03 16:19
  8. */
  9. public class G1Demo {
  10. public static void main(String[] args) {
  11. System.out.println("程序开始");
  12. List<Garbage> list = new ArrayList<>();
  13. long count = 0;
  14. while (true) {
  15. list.add(new Garbage(list.size() + 1));
  16. if (list.size() == 1000000 && count == 0) {
  17. list.clear();
  18. count++;
  19. }
  20. }
  21. }
  22. }
  • 启动参数设置:
  1. -Xms100m -Xmx100m

使用Epsilon垃圾收集器前.png

会发现G1一直回收对象,直到内存不够用。

  • 使用Epsilon垃圾收集器,需要设置启动参数:
  1. -XX:+UnlockExperimentalVMOptions
  2. -XX:+UseEpsilonGC
  3. -Xms100m
  4. -Xmx100m

使用Epsilon垃圾收集器后.png

会发现很快就内存溢出了,因为Epsilon不会去回收对象。

2.9.3 应用场景

  • 性能测试:Epsilon垃圾收集器可以帮助我们过滤掉GC引起的性能假象。
  • 内存压力测试:在测试Java代码的时候,确定分配内存的阈值有助于设置内存压力常量值。
  • 非常短的JOB任务:对象这种任务,接受GC清理堆就是浪费空间。
  • VM接口测试:以VM开发视角,有一个简单的GC实现,有助于理解VM-GC的最小接口实现,它也用于证明VM-GC接口的健壮性。
  • ……

2.10 ZGC可伸缩低延迟垃圾收集器

2.10.1 ZGC的目标

  • GC是Java主要优势之一。然而,当GC停顿太长,就会开始影响应用的响应时间。消除或者减少GC停顿时长,Java将对更广泛的应用场景是一个更有吸引力的平台。此外,现代系统中可用内存不断增长,用户和程序员希望JVM能够以高效的方式充分的利用这些内存,并且无需长时间的GC暂停时间。
  • 今天,应用程序同时为数千甚至数百万用户提供服务的情况并不多见。这些应用程序需要大量内存,但是,管理所欲内存可能会轻易影响应用程序性能。为了解决这个问题,Java11引入了ZGC垃圾收集器作为实验性垃圾收集器的实现。
  • ZGC全称Z Garbage Collector,是一款可伸缩的低延迟、高并发的垃圾回收器,旨在实现以下目标:
  • JDK11 - 图5停顿时间不超过10ms。
  • JDK11 - 图6停顿时间不随heap大小或存活对象大小增大而增大。
  • JDK11 - 图7可以处理从几百G到几TB的内存大小,远远超过前一代的G1。
  • JDK11 - 图8初识只支持64位系统。

2.10.2 用法

  • 运行我们的应用程序时,我可以使用以下命令选项启动ZGC:
  1. -XX:+UnlockExperimentalVMOptions -XX:+UseZGC
  • 需要注意的是,ZGC处于实验阶段。

2.10.3 平台支持

  • ZGC目前只在Linux/x64上可用,如果有足够的需求,将来可能会增加对其他平台的支持。