侧边栏壁纸
  • 累计撰写 106 篇文章
  • 累计创建 19 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

Java11新特性

zero
2022-04-18 / 0 评论 / 0 点赞 / 21 阅读 / 15876 字
温馨提示:
本文最后更新于 2024-07-06,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1、JShell

  • 用过Python的同学都知道,Python中的读取-求值-打印循环(Read-Evaluation-Print-Loop)很方便,它的目的在于以即使结果和反馈的形式。

  • Java9引入JShell这个交互性工具,让Java也可以像脚本语言一样来运行,可以从控制台启动JShell,在JShell中直接输入表达式并查看其执行结果,当需要测试一个方法的运行效果或是快速的对表达式进行求值时,JShell都非常实用。

  • 出了表达式之外,还可以创建Java类和方法,JSell也有基本的代码完成功能,我们在教人们如何编写Java的过程中,不再需要解释“public static void main (String[] args)”这句废话。

2、Dynamic Class-File Constants 类文件新添的一种结构

  • Java的类型文件格式将被拓展,支持一种新的常量池格式:CONSTANT_Dynamic,加载CONSTANT_Dynamic会将创建委托给BootStrap方法。

目标:降低开发新形式的可实现类文件约束带来的成本和干扰

3、局部变量类型推断(var "关键字")

什么是局部变量类型推断?

var javastack = "javastack";
System.out.println(javastack);

大家看出来了,局部变量类型推断就是左边的类型直接食用var定义,而不用写具体的类型,编译器能根据右边的表达式自动推断类型,如上面的 String 。

var javastack = "javastack";
// ==
String javastack = "javastack";

在声明隐式类型的lambda表达式的形参时允许使用var,使用var的好处是在使用lambda表达式时给参数加上注解如:

(@Nonnull var x, @Nullable var y) -> x.process(y);

如果不加注解直接var用于Lambda表达式,显然是解裤子放屁,所以加上注解才是它主要的用途之一:

Consumer<T> : 消费性函数接口
    public void accept(T t);

//直接报错,提示非法的表达式
Consumer<String> consumer = (@Nonnull t) -> System.out.println(t.toUpperCase());

// 完美运行
Consumer<String> consumer = (@Nonnull var t) -> System.out.println(t.toUpperCase());

注意点:

  1. var a; 这样不可以,因为无法推断

  2. 类的属性的数据类型不可以使用var

4、新加的一些实用API

4.1、集合中新API介绍

自Java9开始,JDK里面为集合(List/Set/Map)都添加了of和copyOf方法,它们两个都用来创建不可变的集合,来看一下它们的使用和区别:

// 老方法
@Test
public void test1() {
    List<Stirng> list = new ArrayList<>();
    list.add("aa");
    list.add("bb");
    list.add("cc");
    list.add("dd");
}

// 新方法
@Test
public void test2() {
    //使用新的API则更简单
    List<Stirng> list = List.of("aa","bb","cc","dd");

    //但是不可以使用追加
    list.add("ee") // 会报错,底层直接抛出异常
}

4.2、流中新的API介绍

Stream是Java8种的新特性,Java9开始对Stream增加了以下4个新方法:

/**
 * 流的处理
 * 1、创建流
 * 2、中间操作
 * 3、终止操作 - 打印在此操作
 */

@Test
public void test1() {
    Stream<Integer> stream1 = Stream.of(3 ,9 ,20 ,22 ,40);
    // stream1.forEach(t -> System.out.println(t));
    stream1.forEach(System.out::println);

    //流中没有数据
    Stream<Object> stream2 = Stream.of();
    stream2.forEach(System.out::println);

    //流中传入Null值来创建流对象
    Stream<Object> stream3 = Stream.ofNullable(null);
    stream3.forEach(System.out::println)
}
@Test
public void test2() {
    // 新方法 takeWhile,dropWhile
    // 从流中一直获取判定器为真的元素,一旦遇到元素为假,就终止处理。
    Stream<Integer> stream1 = Stream.of(3 ,9 ,20 ,22 ,40);

    Stream<Integer> stream2 = stream1.takeWhile(t -> t % 2 != 0);
    stream1.forEach(System.out::println);   // 输出:3 ,9

    Stream<Integer> stream2 = stream1.dropWhile(t -> t % 2 != 0);
    stream1.forEach(System.out::println);   // 输出:20 ,22 ,40
}
@Test
public void test2() {
    // 流的迭代,创建流
    //老方法,设置迭代到第十次就结束
    Stream<Integer> stream1 = Stream.iterate(1 , t -> (2 * t) + 1);
    stream1.limit(10).forEach(System.out::println);

    //新方法。设置有限的迭代,值在1000以内则打印
    Stream<Integer> stream2 = Stream.iterate(1 ,t -> t < 1000 ,t -> (2 * t) + 1);
    stream1.limit(10).forEach(System.out::println);

}

4.3、字符串中新的API介绍

如以下所示:

// 判断字符串是否为空
"".isBlank();     // true
//去除首尾空白
" bigbey ".strip();       // "bigbey"
//去除尾部空白
"bigbey ".stripTrailing();        // "bigbey"
//去除首部空白
" bigbey".stripLeading();     // "bigbey"
//复制字符串
"bigbey".repeat(3); "bigbeybigbeybigbey"
//行数统计
"A\nB\nC\n".lines().count();  // 3

4.4、Optional加强

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

// of方法中如果传入的参数是null。会抛出空指针异常
// Optional<String> optional = Optional.of(null);
// ofNullable可以兼容空指针,但是实际传入null后要小心
Optional<Object> optional = Optional.ofNullable(null);
Object object1 = optional.orElse("abc");      // 如果内部引用为空,则返回参数中的引用,否则返回内部引用
System.out.println(object1);

Object object2 = optional.orElseThrow();
System.out.println(object2);    // 抛出NoSuchElementException: No value present

4.5、改进的文件API

InputStream加强
InputStream终于有了一个非常有用的方法:transferTo,可以用来将数据直接传输到OutputStream,这是在处理原始数据流时非常常见的一种用法,如下所示:

@Test
public void testName() throws Exception {
    var cl = this.getClass().getClassaLoader();
    var is = cl.getResourceAsStream("file1");
    try (var or = new FileOutputStream("file2")) {
        is.transferTo(os);      // 把输入流中的所有数据直接自动复制到输出流中
    }
    is.close();
}

5、标准Java异步HTTP客户端

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

@Test
public void testName1() throws Exception {
    //同步
    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);
}

@Test
public void testName2() throws Exception {
    //异步
    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);
    HttpResponse<String> response = sedAsync.get();
    String body = response.body();
    System.out.println(body);
}

6、移除的一些其他内容

移除项:

  1. 移除了com.sun.awt.AWTUtilities

  2. 移除了sun.misc.Unsafe.defineClass,

  3. 使用java.lang.invoke.MethodHandles.Lookup.defineClass来替代

  4. 移除了Thread.destroy()以及 Thread.stop(Throwable)方法

  5. 移除了sun.nio.ch.disableSystemWideOverlappingFileLockCheck、sun.locale.formatasdefault属性

  6. 移除了jdk.snmp模块

  7. 移除了javafx,openjdk估计是从java10版本就移除了,oracle jdk10还尚未移除javafx,而java11版本则oracle的jdk版本也移除了javafx

  8. 移除了Java Mission Control,从JDK中移除之后,需要自己单独下载

  9. 移除了这些Root Certificates :Baltimore Cybertrust Code Signing CA,SECOM ,AOL and Swisscom

废弃项:

  1. -XX+AggressiveOpts选项

  2. -XX:+UnlockCommercialFeatures

  3. -XX:+LogCommercialFeatures选项也不再需要

7、更简化的编译运行程序

JEP 330 : 增强Java启动器支持运行单个Java源代码的程序。

注意点:

  • 执行源文件中的第一个类,第一个类必须包含主方法

  • 不可以使用别的源文件中的自定义类,本文件中的自定义类是可以使用的

在我们的认知里,要运行一个Java源代码必须先编译,然后运行,两步执行动作,则会生成一个class文件,而在Java11中,则通过一个Java命令就直接搞定了,并不会生成class文件

# 编译
javac bigbey.java
#运行
java bigbey
# 改进后
java bigbey.java

8、Unicode 10

Unicode 10 增加了8518个字符,总计达到了136690个字符,并且增加了4个脚本,同时还有56个新的emoji表情符号。

9、Remove the JavaEE and CORBA Moudles

在java11中移除了不太使用的JavaEE模块和CORBA技术
CORBA来自于二十世纪九十年代,Oracle说,现在用CORBA开发现代Java应用程序已经没有意义了,维护CORBA的成本已经超过了保留它带来的好处。

但是删除CORBA将使得那些依赖于JDK提供部分CORBA API的CORBA实现无法运行。目前还没有第三方CORBA版本,也不确定是否会有第三方愿意接手CORBA API的维护工作。

在java11中将java9标记废弃的Java EE及CORBA模块移除掉,具体如下:
(1)xml相关的,

  1. java.xml.ws 被移除,

  2. java.xml.bind 被移除,

  3. java.xml.ws 被移除,

  4. java.xml.ws.annotation 被移除,

  5. jdk.xml.bind 被移除,

  6. jdk.xml.ws被移除,

  7. 只剩下java.xml,java.xml.crypto,jdk.xml.dom这几个模块;

(2)java.corba,

  1. java.se.ee 被移除,

  2. java.activation 被移除,

  3. java.transaction 被移除,

  4. 但是java11新增一个java.transaction.xa模块

10、JEP:335 Deprecate the Nashorn JavaScript Engine

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

11、JEP:336 Deprecate the Pack200 Tools and API

Java5中的一个压缩工具:pack200,这个工具能够压缩jar包,其主要作用就是解决客户端之间传输太大文件慢而出现的一种技术,但是随着现在5G网络的诞生,这个工具则不在好用,而且配置起来也很麻烦所以弃用。

12、Epsilon垃圾收集器

A NoOp Garbage Collector
JDK上对这个特性的描述是: 开发一个处理内存分配但不实现任何实际内存回收机制的GC, 一旦可用堆内存用完, JVM就会退出.
如果有System.gc()调用, 实际上什么也不会发生(这种场景下和-XX:+DisableExplicitGC效果一样), 因为没有内存回收, 这个实现可能会警告用户尝试强制GC是徒劳.

用法 : -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC

class Garbage {
    int n = (int)(Math.random() * 100);
    @Override
    public void finalize() {
        System.out.println(this + " : " + n + " is dying");
    }
}
public class EpsilonTest {
    public static void main(String[] args) {
        boolean flag = true;
        List<Garbage> list = new ArrayList<>();
        long count = 0;
        while (flag) {
            list.add(new Garbage());
            if (list.size() == 1000000 && count == 0) {
                list.clear();
                count++;
            }
        }
        System.out.println("程序结束");
    }
}

如果使用选项-XX:+UseEpsilonGC, 程序很快就因为堆空间不足而退出

使用这个选项的原因 :
提供完全被动的GC实现, 具有有限的分配限制和尽可能低的延迟开销,但代价是内存占用和内存吞吐量.
众所周知, java实现可广泛选择高度可配置的GC实现. 各种可用的收集器最终满足不同的需求, 即使它们的可配置性使它们的功能相交. 有时更容易维护单独的实现, 而不是在现有GC实现上堆积另一个配置选项.

主要用途如下 :

  1. 性能测试(它可以帮助过滤掉GC引起的性能假象)

  2. 内存压力测试(例如,知道测试用例 应该分配不超过1GB的内存, 我们可以使用-Xmx1g –XX:+UseEpsilonGC, 如果程序有问题, 则程序会崩溃)

  3. 非常短的JOB任务(对象这种任务, 接受GC清理堆那都是浪费空间)

  4. VM接口测试 Last-drop 延迟&吞吐改进

13、ZGC垃圾收集器

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

  • ZGC, A Scalable Low-Latency Garbage Collector(Experimental)

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

  • GC暂停时间不会超过10ms

  • 既能处理几百兆的小堆, 也能处理几个T的大堆(OMG)

  • 和G1相比, 应用吞吐能力不会下降超过15%

  • 为未来的GC功能和利用colord指针以及Load barriers优化奠定基础

  • 初始只支持64位系统

ZGC的设计目标是:支持TB级内存容量,暂停时间低(小于10ms),对整个程序吞吐量的影响小于15%。 将来还可以扩展实现机制,以支持不少令人兴奋的功能,例如多层堆(即热对象置于DRAM和冷对象置于NVMe闪存),或压缩堆。

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

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

 

avg

max

ZGC

1.091ms

1.681ms

G1

156.806ms

543.846

用法 : -XX:+UnlockExperimentalVMOptions –XX:+UseZGC, 因为ZGC还处于实验阶段, 所以需要通过JVM参数来解锁这个特性,只支持Linux系统。

14、完全支持Linux容器(包括Docker)

许多运行在Java虚拟机中的应用程序(包括Apache Spark和Kafka等数据服务以及传统的企业应用程序)都可以在Docker容器中运行。但是在Docker容器中运行Java应用程序一直存在一个问题,那就是在容器中运行JVM程序在设置内存大小和CPU使用率后,会导致应用程序的性能下降。这是因为Java应用程序没有意识到它正在容器中运行。随着Java 10的发布,这个问题总算得以解决,JVM现在可以识别由容器控制组(cgroups)设置的约束。可以在容器中使用内存和CPU约束来直接管理Java应用程序,其中包括:

  1. 遵守容器中设置的内存限制

  2. 在容器中设置可用的CPU

  3. 在容器中设置CPU约束

15、支持G1上的并行完全垃圾收集。

对于 G1 GC,相比于 JDK 8,升级到 JDK 11 即可免费享受到:并行的 Full GC,快速的 CardTable 扫描,自适应的堆占用比例调整(IHOP),在并发标记阶段的类型卸载等等。这些都是针对 G1 的不断增强,其中串行 Full GC 等甚至是曾经被广泛诟病的短板,你会发现 GC 配置和调优在 JDK11 中越来越方便。

16、JEP 331 : Low-Overhead Heap Profiling免费的低耗能飞行记录仪和堆分析仪。

通过JVMTI的SampledObjectAlloc回调提供了一个开销低的heap分析方式

提供一个低开销的, 为了排错java应用问题, 以及JVM问题的数据收集框架, 希望达到的目标如下 :

  1. 提供用于生产和消费数据作为事件的API

  2. 提供缓存机制和二进制数据格式

  3. 允许事件配置和事件过滤

  4. 提供OS,JVM和JDK库的事件

17、JEP 329 : 实现RFC7539中指定的ChaCha20和Poly1305两种加密算法, 代替RC4

实现 RFC 7539的ChaCha20 and ChaCha20-Poly1305加密算法

RFC7748定义的秘钥协商方案更高效, 更安全. JDK增加两个新的接口
XECPublicKey 和 XECPrivateKey

KeyPairGenerator kpg = KeyPairGenerator.getInstance(“XDH”);
NamedParameterSpec paramSpec = new NamedParameterSpec(“X25519”);
kpg.initialize(paramSpec);
KeyPair kp = kgp.generateKeyPair();

KeyFactory kf = KeyFactory.getInstance(“XDH”);
BigInteger u = new BigInteger(“xxx”);
XECPublicKeySpec pubSpec = new XECPublicKeySpec(paramSpec, u);
PublicKey pubKey = kf.generatePublic(pubSpec);

KeyAgreement ka = KeyAgreement.getInstance(“XDH”);
ka.init(kp.getPrivate());
ka.doPhase(pubKey, true);
byte[] secret = ka.generateSecret();

18、新的默认根权限证书集。

19、最新的HTTPS安全协议TLS 1.3。

实现TLS协议1.3版本, TLS允许客户端和服务器端通过互联网以一种防止窃听, 篡改以及消息伪造的方式进行通信.

20、Java Flight Recorder

Flight Recorder源自飞机的黑盒子

Flight Recorder以前是商业版的特性,在java11当中开源出来,它可以导出事件到文件中,之后可以用Java Mission Control来分析。可以在应用启动时配置java -XX:StartFlightRecording,或者在应用启动之后,使用jcmd来录制,比如

  1. $ jcmd JFR.start

  2. $ jcmd JFR.dump filename=recording.jfr

  3. $ jcmd JFR.stop

是 Oracle 刚刚开源的强大特性。我们知道在生产系统进行不同角度的 Profiling,有各种工具、框架,但是能力范围、可靠性、开销等,大都差强人意,要么能力不全面,要么开销太大,甚至不可靠可能导致 Java 应用进程宕机。

而 JFR 是一套集成进入 JDK、JVM 内部的事件机制框架,通过良好架构和设计的框架,硬件层面的极致优化,生产环境的广泛验证,它可以做到极致的可靠和低开销。在 SPECjbb2015 等基准测试中,JFR 的性能开销最大不超过 1%,所以,工程师可以基本没有心理负担地在大规模分布式的生产系统使用,这意味着,我们既可以随时主动开启 JFR 进行特定诊断,也可以让系统长期运行 JFR,用以在复杂环境中进行“After-the-fact”分析。还需要苦恼重现随机问题吗?JFR 让问题简化了很多。

在保证低开销的基础上,JFR 提供的能力也令人眼前一亮,例如:我们无需 BCI 就可以进行 Object Allocation Profiling,终于不用担心 BTrace 之类把进程搞挂了。对锁竞争、阻塞、延迟,JVM GC、SafePoint 等领域,进行非常细粒度分析。甚至深入 JIT Compiler 内部,全面把握热点方法、内联、逆优化等等。JFR 提供了标准的 Java、C++ 等扩展 API,可以与各种层面的应用进行定制、集成,为复杂的企业应用栈或者复杂的分布式应用,提供 All-in-One 解决方案。而这一切都是内建在 JDK 和 JVM 内部的,并不需要额外的依赖,开箱即用。

0

评论区