【JAVA高级&JDK10&11新特性】知识点整理

【JAVA高级&JDK10&11新特性】知识点整理

概述

通过本章节的知识点整理,复盘在 JDK10JDK11 中提供的一些新特性、新增的一些常用的 API 接口以及部分 API 的一些语法上的更新。

目录

主要内容

0x01:JDK10

需要注意的是 Java 9Java 10 都不是 LTS (Long-Term-Support) 版本。和过去的 Java 大版本升级不同,这两个只有半年左右的开发和维护期。而未来的 Java 11,也就是 18.9 LTS,才是 Java 8 之后第一个 LTS 版本。

JDK10一共定义了 109 个新特性, 其中包含12个JEP(对于程序员来讲, 真
正的新特性其实就一个) , 还有一些新 APIJVM 规范以及 JAVA语言规范上的改动。

JDK10的12个JEP

  • 286: Local-Variable Type Inference 局部变量类型推断
  • 296: Consolidate the JDK Forest into a Single Repository JDK库的合并
  • 304: Garbage-Collector Interface 统一的垃圾回收接口
  • 307: Parallel Full GC for G1 为G1提供并行的Full GC
  • 310: Application Class-Data Sharing 应用程序类数据(AppCDS)共享
    312: Thread-Local Handshakes ThreadLocal握手交互
  • 313: Remove the Native-Header Generation Tool (javah) 移除JDK中附带的javah工具
  • 314: Additional Unicode Language-Tag Extensions 使用附加Unicode语言标记扩展
  • 316: Heap Allocation on Alternative Memory Devices 能将堆内存占用分配给用户指定的备用内存设备
  • 317: Experimental Java-Based JIT Compiler 使用基于Java的JIT编译器
  • 319: Root Certificates 根证书
  • 322: Time-Based Release Versioning 基于时间的发布版本

局部类型的推断

使用举例 day13/java1/Java10Test.java

产生背景

开发者经常抱怨Java中引用代码的程度。 局部变量的显示类型声明,常常被认为是不必须的,给一个好听的名字经常可以很清楚的表达出下面应该怎样继续。

好处

减少了啰嗦和形式的代码,避免了信息冗余,而且对齐了变量名,更容易阅读!

举例如下:

场景一: 类实例化时

作为 Java 开发者, 在声明一个变量时, 我们总是习惯了敲打两次变量类型, 第一次用于声明变量类型, 第二次用于构造器。

LinkedHashSet<Integer> set = new LinkedHashSet<>();

场景二: 返回值类型

含复杂泛型结构变量的声明类型书写复杂且较长,尤其是加上泛型的使用

LinkedHashSet<Integer> set = new LinkedHashSet<>();
Iterator<Map.Entry<Integer, Student>> iterator = set.iterator();

尽管 IDE 可以帮我们自动完成这些代码,但当变量总是跳来跳去的时候,可读性还是会受到影响,因为变量类型的名称由各种不同长度的字符组成。而且,有时候开发人员会尽力避免声明中间变量,因为太多的类型声明只会分散注意力,不会带来额外的好处。

基本使用

一个例子,如下

//1.声明变量时,根据所附的值,推断变量的类型
var num = 10;

var list = new ArrayList<Integer>();
list.add(123);

//2.遍历操作
for (var i : list) {
    System.out.println(i);
    System.out.println(i.getClass());
}

//3.普通的遍历操作
for (var i = 0; i < 100; i++) {
    System.out.println(i);
}

在局部变量中使用时,如下情况不适用:

  • 初始值为 null

  • 方法引用

  • Lambda 表达式

  • 为数组静态初始化

不适用以下的结构中:

  • 情况1: 没有初始化的局部变量声明
  • 情况2: 方法的返回类型
  • 情况3: 方法的参数类型
  • 情况4: 构造器的参数类型
  • 情况5: 属性
  • 情况6: catch块

工作原理

在处理 var 时, 编译器先是查看表达式右边部分, 并根据右边变量值的类型进行推断, 作为左边变量的类型, 然后将该类型写入字节码当中。

注意:

  • var 不是一个关键字
    你不需要担心变量名或方法名会与 var发生冲突, 因为 var实际上并不是一个关键字,而是一个类型名, 只有在编译器需要知道类型的地方才需要用到它。 除此之外, 它就是一个普通合法的标识符。 也就是说, 除了不能用它作为类名, 其他的都可以,但极少人会用它作为类名。

  • 这不是 JavaScript

    首先我要说明的是, var并不会改变 Java 是一门静态类型语言的事实。 编译器负责推断出类型, 并把结果写入字节码文件, 就好像是开发人员自己敲入类型一样。

下面是使用 IntelliJ(实际上是 Fernflower的反编译器)反编译器反编译出的代码:

从代码来看,就好像之前已经声明了这些类型一样。事实上,这一特性只发
生在编译阶段,与运行时无关,所以对运行时的性能不会产生任何影响。所
以请放心,这不是 JavaScript

新增copyOf()方法,用于创建只读集合

Java 9 开始, Jdk 里面为集合(List / Set / Map) 都添加了 of (jdk9新增) 和 copyOf (jdk10新增)方法, 它们两个都用来创建不可变的集合, 来看下它们的使用和区别。

//示例1:
var list1 = List.of("Java", "Python", "C");
var copy1 = List.copyOf(list1);
System.out.println(list1 == copy1); // true
//示例2:
var list2 = new ArrayList<String>();
var copy2 = List.copyOf(list2);
System.out.println(list2 == copy2); // false

示例1和2代码基本一致, 为什么一个为true,一个为false?

  • 从源码分析 , 可以看出 copyOf 方法会先判断 来源集合是不是AbstractImmutableList 类型的, 如果是, 就直接返回, 如果不是, 则调用 of 创建一个新的集合。
  • 示例2 因为用的 new 创建的集合, 不属于不可变 AbstractImmutableList 类的子类,所以 copyOf 方法又创建了一个新的实例, 所以为 false

注意:使用 ofcopyOf 创建的集合为不可变集合, 不能进行添加、 删除、 替换、排序等操作, 不然会报java.lang.UnsupportedOperationException 异常。

上面演示了 ListofcopyOf 方法, SetMap 接口都有

0x02:JDK11

概述

JDK 11 将是一个 企业不可忽视的版本。 从时间节点来看, JDK 11 的发布正好处在 JDK 8 免费更新到期的前夕, 同时 JDK 9、 10 也陆续成为“历史版本” , 下面是 Oracle JDK 支持路线图:

JDK 11 是一个长期支持版本(LTS, Long-Term-Support)

  • 对于企业来说, 选择 11 将意味着长期的、 可靠的、 可预测的技术路线图。其中免费的 OpenJDK11 确定将得到 OpenJDK 社区的长期支持, LTS 版本将可以放心选择的版本。
  • JVM GC 的角度, JDK11 引入了两种新的 GC, 其中包括也许是划时代意义的 ZGC, 虽然其目前还是实验特性, 但是从能力上来看, 这是 JDK 的一个巨大突破, 为特定生产环境的苛刻需求提供了一个可能的选择。 例如, 对部分企业核心存储等产品, 如果能够保证不超过 10msGC 暂停, 可靠性会上一个大的台阶, 这是过去我们进行 GC 调优几乎做不到的, 是能与不能的问题。

新特性一:String中新增的一些方法

描述举例
判断字符串是否为空白" ".isBlank(); // true
去除首尾空白" Javastack ".strip(); // "Javastack"
去除尾部空格" Javastack ".stripTrailing(); // " Javastack"
去除首部空格" Javastack ".stripLeading(); // "Javastack "
复制字符串"Java".repeat(3);// "JavaJavaJava"
行数统计"A\nB\nC".lines().count(); // 3

示例代码如下

//java 11新特性一:String中新增的方法
@Test
public void test1(){
    //        isBlank():判断字符串是否为空白
    System.out.println("  \t  \t  \n  ".isBlank());
    //        strip():去除首尾空白
    System.out.println("-----" + "  \t abc \t  \n  ".strip() + "-------");
    System.out.println("-----" + "  \t abc \t  \n  ".trim() + "-------");
    //        stripTrailing():去除尾部空格
    System.out.println("-----" + "  \t abc \t  \n  ".stripTrailing() + "-------");
    //        stripLeading():去除首部空格
    System.out.println("-----" + "  \t abc \t  \n  ".stripLeading() + "-------");
    //        repeat(int count):复制字符串
    String str1 = "abc";
    String str2 = str1.repeat(5);
    System.out.println(str2);

    //        lines().count():行数统计
    String str3 = "abc\ndef\ng";
    System.out.println(str3.lines().count());

}

新特性二:Optional加强

Optional 也增加了几个非常酷的方法, 现在可以很方便的将一个 Optional 转换成一个 Stream, 或者当一个空 Optional 时给它一个替代的。

新增方法描述新增的版本
boolean isEmpty()判断value是否为空JDK 11
ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)value非空,执行参数1功能;如果value 为空,执行参数2功能JDK 9
Optional or(Supplier<? extends Optional<? extends T>> supplier)value非空,返回对应的Optional; value为空,返回形参封装的OptionalJDK 9
Stream stream()value非空,返回仅包含此value的 Stream;否则,返回一个空的StreamJDK 9
T orElseThrow()value非空,返回value;否则抛异常 NoSuchElementExceptionJDK 10

新特性三:局部变量类型推断的升级

var 上添加注解的语法格式,在 jdk10 中是不能实现的。在 JDK11 中加入了这样的语法。

//错误的形式: 必须要有类型, 可以加上var
//Consumer<String> con1 = (@Deprecated t) ->
System.out.println(t.toUpperCase());
//正确的形式:
//使用var的好处是在使用lambda表达式时给参数加上注解。
Consumer<String> con2 = (@Deprecated var t) ->
    System.out.println(t.toUpperCase());

新特性四:新增HttpClient替换原有的HttpURLConnection

这是Java9开始引入的一个处理HTTP请求的的HTTPClientAPI,该
API支持同步和异步,而在Java11中已经为正式可用状态,你可以在java.net包中找到这个API

它将替代仅适用于blocking模式的 HttpURLConnection,并提供对 WebSocketHTTP/2的支持。

HttpURLConnection 是在 HTTP1.0的时代创建的,并使用了协议无关的方法

示例代码1:

HttpClient client = HttpClient.newHttpClient();
HttpRequest request =
HttpRequest.newBuilder(URI.create("http://127.0.0.1:8080/test/")).build();
BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();
HttpResponse<String> response = client.send(request, responseBodyHandler);
String body = response.body();
System.out.println(body);

示例代码2:

HttpClient client = HttpClient.newHttpClient();
HttpRequest request =
    HttpRequest.newBuilder(URI.create("http://127.0.0.1:8080/test/")).build();
BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();
CompletableFuture<HttpResponse<String>> sendAsync =
    client.sendAsync(request, responseBodyHandler);
sendAsync.thenApply(t -> t.body()).thenAccept(System.out::println);
//HttpResponse<String> response = sendAsync.get();
//String body = response.body();
//System.out.println(body);

新特性五:更简化的编译运行程序

看下面的代码。

// 编译
javac Javastack.java
// 运行
java Javastack

在我们的认知里面, 要运行一个 Java 源代码必须先编译, 再运行, 两步执行动作。

而在 Java 11 版本中, 通过一个 java 命令就直接搞定了, 如以下所示:java Javastack.java

但是需要注意:

  • 执行源文件中的第一个类, 第一个类必须包含主方法。
  • 并且不可以使用其它源文件中的自定义类, 本文件中的自定义类是可以使用的。

新特性六:废弃Nashorn引擎

废除 Nashorn javascript引擎, 在后续版本准备移除掉, 有需要的可以考虑使用 GraalVM

新特性七:ZGC

GCjava主要优势之一。 然而, 当 GC 停顿太长, 就会开始影响应用的响应时间。 消除或者减少 GC 停顿时长, java 将对更广泛的应用场景是一个更有吸引力的平台。 此外, 现代系统中可用内存不断增长,用户和程序员希望JVM 能够以高效的方式充分利用这些内存, 并且无需长时间的 GC 暂停时间

ZGC, 这应该是 JDK11 最为瞩目的特性, 没有之一。 但是后面带了Experimental , 说明这还不建议用到生产环境。

ZGC 是一个并发, 基于 region, 压缩型的垃圾收集器, 只有 root 扫描阶段会 STW(stop the world), 因此 GC 停顿时间不会随着堆的增长和存活对象的增长而变长。

优势:

  • GC 暂停时间不会超过 10ms
  • 既能处理几百兆的小堆, 也能处理几个T的大堆(OMG)
  • G1 相比, 应用吞吐能力不会下降超过 15%
  • 为未来的 GC功能和利用 colord 指针以及 Load barriers 优化奠定基础
  • 初始只支持 64 位系统
  • ZGC 的设计目标是:支持 TB 级内存容量, 暂停时间低(<10ms) , 对整个程序吞吐量的影响小于 15%。 将来还可以扩展实现机制, 以支持不少令人兴奋的功能, 例如多层堆(即热对象置于DRAM和冷对象置于NVMe闪存) ,或压缩堆。

新特性八:其它新特性

  • Unicode 10
  • Deprecate the Pack200 Tools and API
  • 新的Epsilon垃圾收集器
  • 完全支持Linux容器(包括Docker)
  • 支持G1上的并行完全垃圾收集
  • 最新的HTTPS安全协议TLS 1.3
  • Java Flight Recorder

在当前JDK中看不到什么?

  • 一个标准化和轻量级的JSON API

    一个标准化和轻量级的 JSON API 被许多 Java 开发人员所青睐。 但是由于资金问题无法在 Java 当前版本中见到, 但并不会削减掉。 Java 平台首席架构师MarkReinholdJDK 9 邮件列中说:

    “这个 JEP 将是平台上的一个有用的补充, 但是在计划中, 它并不像Oracle 资助的其他功能那么重要, 可能会重新考虑 `JDK 10 或更高版本中实现。 ”

  • 新的货币 API

    对许多应用而言货币价值都是一个关键的特性, 但JDK对此却几乎没有任何支持。严格来讲, 现有的java.util.Currency类只是代表了当前ISO 4217货币的一个数据结构,但并没有关联的值或者自定义货币。 JDK对货币的运算及转换也没有内建的支持,更别说有一个能够代表货币值的标准类型了。

    此前, Oracle 公布的 JSR 354 定义了一套新的 Java货币API: JavaMoney, 计划会在 Java9 中正式引入。 但是目前没有出现在JDK 新特性中。

    不过,如果你用的是 Maven的话,可以添加以下的依赖,即可使用相关的API 处理货币:

    <dependency>
        <groupId>org.javamoney</groupId>
        <artifactId>moneta</artifactId>
        <version>0.9</version>
    </dependency>
    

0xFF:展望

  • 随着云计算和 AI 等技术浪潮, 当前的计算模式和场景正在发生翻天覆地的变化, 不仅对 Java 的发展速度提出了更高要求, 也深刻影响着 Java 技术的发展方向。 传统的大型企业或互联网应用, 正在被云端、 容器化应用、 模块化的微服务甚至是函数 (FaaS, Function-as-a-Service)所替代。
  • Java 虽然标榜 "面向对象编程",却毫不顾忌的加入面向接口编程思想,又扯出"匿名对象之概念",每增加一个新的东西,对 Java的根本所在的面向对象思想的一次冲击。反观 Python,抓住面向对象的本质,又能在"函数编程"思想方面游刃有余。
  • Java 对标 C/C++,以抛掉内存管理为卖点,却又陷入了 JVM 优化的噩梦。选择比努力更重要,选择 Java 的人更需要对它有更清晰的认识。
  • Java 需要在新的计算场景下,改进开发效率。 Java 代码虽然进行了一些类型推断等改进,更易用的集合 API 等,但仍然给开发者留下了过于刻板、形式主义的印象,这是一个长期的改进方向。

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://codeyee.com/archives/java-jdk10jdk11.html