Java 11 新特性

java 11 是继 java8 之后的第一个LTS版本。因此有必要针对它进行一些深入的学习,虽然短时间内java8 还是主流版本。当然,如果从java8基础上升级,几乎可以确定目标就是java11。

同时也要明确一个问题,现在java的版本升级周期与前些年相比速度快了太多,对于应用开发者来说没必要每一个小版本都去花时间研究,比如这些过渡版本:java9、java10、java12、java13(至少目前还不是LTS版本),了解即可。

下面梳理一下 java11 的新特性。

181: Nest-Based Access Control(基于嵌套的访问控制)

http://openjdk.java.net/jeps/181

规范中提到了一个例子:

We will adjust the JVM's access rules by adding something like the following clause to JVMS 5.4.4:

A field or method R is accessible to a class or interface D if and only if any of the following conditions are true:

  ...
  R is private and is declared in a different class or interface C, and C and D, are nestmates.

For types C and D to be nestmates they must have the same nest host. A type C claims to be a member of the nest hosted by D, if it lists D in its NestHost attribute. The membership is validated if D also lists C in its NestMembers attribute. D is implicitly a member of the nest that it hosts.

A class with no NestHost or NestMembers attribute, implicitly forms a nest with itself as the nest host, and sole nest member.

简单的翻译一下重点部分:

当且仅当以下条件为真时,一个成员字段或方法R是可以被类或接口D访问的:

  ...
  R 是私有的,并且是声明在另一个类或接口C中,同时 C 和 D 是嵌套伙伴。

由于C和D是嵌套同伴,那么他们一定有同一个嵌套宿主类,在这里是Test。如果C在自己的嵌套宿主的属性中可以列举出D,那么C类就会被D 断言为自己的嵌套成员。如果D也可以在自己的NestMembers 属性中列举出C,那么这个嵌套同伴关系就是有效的。D是自己的隐性嵌套成员。

一个类如果没有 NestHost 或 NestMembers 属性,就会隐性地把自己做为嵌套宿主,以及唯一的嵌套成员。(这和上一句是一致的)

巴拉了一堆,看一个例子:

import java.lang.reflect.Field;

public class Test {
	public static void main(String[] args) throws Exception {
    System.out.println("---D 的嵌套宿主");
    System.out.println(D.class.getNestHost());
		System.out.println("---D 的嵌套成员");
		for(Class cls:D.class.getNestMembers()) System.out.println(cls);
    System.out.println("---C 的嵌套宿主");
    System.out.println(C.class.getNestHost());
		System.out.println("---C 的嵌套成员");
		for(Class cls:C.class.getNestMembers()) System.out.println(cls);
		System.out.println("---");
	}

	static class C{
	}
	static class D{
	}
}

执行结果:

---D 的嵌套宿主
class Test
---D 的嵌套成员
class Test
class Test$D
class Test$C

---C 的嵌套宿主
class Test
---C 的嵌套成员
class Test
class Test$D
class Test$C
---

从该实例中可以看出:

  • C 和 D 是 Test 的嵌套成员类,而Test是 前两者的嵌套宿主,这是显而易见的。
  • C 可以在其NestHost(即Test)中列举出D成员,因此他会被加入到D的嵌套成员列表中。
  • D 也同上。

继续来研究这个话题,java 11 解决了什么问题?

import java.lang.reflect.Field;

public class Test {
	public static void main(String[] args) throws Exception {
    D d=new D();
    d.R();
	}

	static class C{
		private int flag=0;
	}
	static class D{
		public void R() throws Exception {
      C c=new C();
      c.flag=1;
      System.out.println(c.flag);// ① 这里没有问题

      Field f = C.class.getDeclaredField("flag");
      f.setInt(c, 2);
      System.out.println(c.flag);//② jdk8 抛出 IllegalAccessException,jdk11 正常
		}
	}
}

在java8中,注释① 处的代码是没有问题的,因为嵌套类是可以访问别的嵌套类的私有属性的。

但注释② 处的代码会抛出异常,这是一个令人看困惑的问题。java11 修复了这个问题,以上代码在java11中执行正常。

309: Dynamic Class-File Constants(动态类文件常量)

http://openjdk.java.net/jeps/309

这是针对类加载机制的修改,增加了一个名为 CONSTANT_Dynamic 的常量池实体。CONSTANT_Dynamic常量池条目对bootstrap方法进行编码,以执行解析(一个方法句柄)、常量的类型(一个类)和任何静态bootstrap参数(常量的任意序列,在动态常量之间的常量池中排除周期)。

该方案是为了降低创建新形式类文件常量的代价和干扰,为语言设计者和编译器开发者提供更广泛的表现形式以及性能。

315: Improve Aarch64 Intrinsics(改进 Aarch64 内联函数 )

http://openjdk.java.net/jeps/315

AArch64是ARMv8 架构的一种执行状态。

在AArch64 CPU指令集中,改进了存在的string和array的内联函数,包括:String::compareTo, String::indexOf, StringCoding::hasNegatives, Arrays::equals, StringUTF16::compress, StringLatin1::inflate 以及多样的 checksum 计算方法。

实现了 java.lang.Mathsin, cos 以及 log() 等函数。

318: Epsilon: A No-Op Garbage Collector(Epsilon — 一个无操作的垃圾收集器)

http://openjdk.java.net/jeps/318

开发了一个GC,它处理内存分配,但不实现任何实际的内存回收机制。一旦可用的Java堆耗尽,JVM就会关闭。

Epsilon GC 通过 -XX:+UseEpsilonGC 开关启用。

320: Remove the Java EE and CORBA Modules(删除 Java EE 和 CORBA 模块)

http://openjdk.java.net/jeps/320

从Java SE平台和jdk中移除了 Java EECORBA 模块,他们在java9中已经标记为 @Deprecated 了。

模块包括:JAX-WS、JAXB、JAF 和 Common Annotation.

具体的技术组件:

java.xml.ws (JAX-WS, plus the related technologies SAAJ and Web Services Metadata)
java.xml.bind (JAXB)
java.activation (JAF)
java.xml.ws.annotation (Common Annotations)
java.corba (CORBA)
java.transaction (JTA)

相关的包:

java.se.ee (Aggregator module for the six modules above)
jdk.xml.ws (Tools for JAX-WS)
jdk.xml.bind (Tools for JAXB)

共计九个jmods:

Their source code will be deleted from the OpenJDK repository.
Their classes will not exist in the JDK runtime image.
Their tools will no longer be available:
wsgen and wsimport (from jdk.xml.ws)
schemagen and xjc (from jdk.xml.bind)
idlj, orbd, servertool, and tnamesrv (from java.corba)
The JNDI CosNaming provider (from java.corba) will no longer be available.
No command line flag will be capable of enabling them, as --add-modules does on JDK 9.

321: HTTP Client (Standard)

http://openjdk.java.net/jeps/321

HTTPClient 是在java9引入孵化的项目,在java10中通过JEP110进行了更新,最终在java11通过JEP321 形成标准。

该API通过 CompletableFutures 实现了非阻塞的request和response功能。

背压和流控制是在 java.util.concurrent.Flow API中通过 reactive-streams 平台实现的。

JEP 321 几乎完全重写了以前的孵化API,实现了完全的异步(以前的HTTP/1.1的实现是阻塞的)。

模块名和包名是: java.net.http

同步请求用例
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.time.Duration;
import java.nio.file.Path;
import java.nio.file.Paths;

public class HttpSyncDemo{
    public static void main(String[] args) throws Exception {
       HttpClient httpClient = HttpClient.newHttpClient();
       HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("http://www.baidu.com"))
            .timeout(Duration.ofSeconds(20))
            .header("Content-Type", "text/html")
            .build();
       HttpResponse<Path> response =
            httpClient.send(request, HttpResponse.BodyHandlers.ofFile(Paths.get("baidu.txt")));
       System.out.println("Response status code: " + response.statusCode());
       System.out.println("Response headers: " + response.headers());
       System.out.println("Response body: " + response.body());
    }
}
异步请求用例
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.util.concurrent.CompletableFuture;
import java.net.http.HttpResponse;
import java.net.URI;
import java.time.Duration;
import java.nio.file.Path;
import java.nio.file.Paths;

public class HttpAsyncDemo{
    public static void main(String[] args) throws Exception {
       HttpClient httpClient = HttpClient.newHttpClient();
       HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("http://www.baidu.com"))
            .timeout(Duration.ofSeconds(20))
            .header("Content-Type", "text/html")
            .build();
       CompletableFuture<String> response =
            httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                .thenApply(HttpResponse::body);
       response.whenComplete((resp, t) -> {
            if (t != null) {
                System.out.println("response message:" + t.getMessage());
            } else {
                System.out.println("response:" + resp);
            }
        }).join();
    }
}

323: Local-Variable Syntax for Lambda Parameters(用于 Lambda 参数的局部变量语法)

在java10 中已经引入了本地变量的类型推断:

var greeting="helloworld!";
System.out.println(greeting);

在java11中类型推断得到了增强,开发者可以使用 var 声明 lambda 参数。

// Inference von Lambda-Parametern
Consumer<String> printer = (var s) -> System.out.println(s); // statt s -> System.out.println(s);

324: Key Agreement with Curve25519 and Curve448(Curve25519 和 Curve448 算法的密钥协议)

http://openjdk.java.net/jeps/324

密钥交换协议规范 RFC 7748 定义了比 elliptic curve Diffie-Hellman (ECDH) 更高效、更安全的密钥协议模式,JEP324是该协议的具体实现。

KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH");
NamedParameterSpec paramSpec = new NamedParameterSpec("X25519");
kpg.initialize(paramSpec); // equivalent to kpg.initialize(255)
// alternatively: kpg = KeyPairGenerator.getInstance("X25519")
KeyPair kp = kpg.generateKeyPair();

KeyFactory kf = KeyFactory.getInstance("XDH");
BigInteger u = ...
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();

什么是密码交换协议?为了数据交互的安全,当客户端和服务端进行交互前,二者需要和对方约定一个密码用来加密解密,那么这个交换密钥(握手)的过程中如何保证密钥不被拦截?这就是密码交换协议解决的范畴。

327: Unicode 10

http://openjdk.java.net/jeps/327

升级平台API,以支持 unicode规范 的 10.0 版本。

设计的类有:java.lang.Characterjava.lang.Stringjava.awt.font.NumericShaperjava.text.Bidijava.text.BreakIterator 以及 java.text.Normalizer

该实现增加了 16,018 个字符,和10个新的脚本。

328: Flight Recorder (飞行记录器)

http://openjdk.java.net/jeps/328

为java应用程序和 HotSpot JVM 提供了一个低开销的排错框架。

发送事件:

import jdk.jfr.*;

public class Hello{
@Label("Hello World")
@Description("Helps the programmer getting started")
static class HelloWorld extends Event {
   @Label("Message")
   String message;
}

public static void main(String... args) throws Exception {
    HelloWorld event = new HelloWorld();
    event.message = "hello, world!";
    event.commit();
    Thread.sleep(30*60*1000);
}
}

执行如下命令在启动应用程序时记录:

$ java -XX:StartFlightRecording Hello
Started recording 1. No limit specified, using maxsize=250MB as default.

Use jcmd 6120 JFR.dump name=1 filename=FILEPATH to copy recording data to file.

编码方式订阅事件:

import java.nio.file.*;
import jdk.jfr.consumer.*;

public class Consumer{
  public static void main(String... args) throws Exception{
    Path p = Paths.get("recording.jfr");
    for (RecordedEvent e : RecordingFile.readAllEvents(p)) {
      try{
        System.out.println(e.getStartTime() + " : " + e.getValue("message"));
      }catch(Exception ee){
      //ee.printStackTrace();
      }
    }
  }
}

输出:

2019-10-18T08:20:37.755218322Z : hello, world!

根据提示执行jcmd 完成记录。

$ jcmd <pid> JFR.start
$ jcmd <pid> JFR.dump filename=recording.jfr
$ jcmd <pid> JFR.stop

329: ChaCha20 and Poly1305 Cryptographic Algorithms(ChaCha20 和 Poly1305 加密算法)

http://openjdk.java.net/jeps/329

实现了 RFC 7539 规范中的 ChaCha20 和 ChaCha20-Poly1305 两种加密算法。

ChaCha20 是新式的流加密算法,可以代替陈旧的,不太安全的 RC4 流加密算法。

ChaCha20 测试:

// Get a Cipher instance and set up the parameters
// Assume SecretKey "key", 12-byte nonce "nonceBytes" and plaintext "pText"
// are coming from outside this code snippet
Cipher mambo = Cipher.getInstance("ChaCha20");
ChaCha20ParameterSpec mamboSpec
    = new ChaCha20ParameterSpec(nonceBytes, 7);   // Use a starting counter value of "7"
// Encrypt our input
mambo.init(Cipher.ENCRYPT_MODE, key, mamboSpec);
byte[] encryptedResult = mambo.doFinal(pText);

ChaCha20-Poly1305 测试:

// Get a Cipher instance and set up the parameters
// Assume SecretKey "key", 12-byte nonce "nonceBytes" and plaintext "pText"
// are coming from outside this code snippet
Cipher mambo = Cipher.getInstance("ChaCha20-Poly1305");
AlgorithmParameterSpec mamboSpec = new IvParameterSpec(nonceBytes);

// Encrypt our input
mambo.init(Cipher.ENCRYPT_MODE, key, mamboSpec);
byte[] encryptedResult = new byte[mambo.getOutputSize(pText.length)];
mambo.doFinal(pText, 0, pText.length, encryptedResult);

330: Launch Single-File Source-Code Programs(启动单一文件的源代码程序)

可以启动尚未编译的类文件。

java HelloWorld.java

它相当于:

javac -d <memory> HelloWorld.java
java -cp <memory> HelloWorld

同时,java11也引入了 Shebang(#!)机制可以直接编写java可执行脚本,如:以下是 demo.sh 的脚本:

#!/usr/local/jdk/bin/java --source 11

public class HelloWorld{
  public static void main(String[] args){
    System.out.println("helloworld");
  }
}

执行:

# chmod +x demo.sh
# ./demo.sh
helloworld
  • 如果你对shell编程比较熟悉,你可能希望使用env来避免使用java路径的硬编码,但这是错误的。

env的语法:

#!/usr/bin/env 脚本解释器名称

如果编写如下脚本:

#!/usr/bin/env java --source 11

执行会出现错误,因为 java --source 11 被当做了一个整体传递给了 env.

331: Low-Overhead Heap Profiling(低开销的 Heap Profiling)

http://openjdk.java.net/jeps/331

提供了一个低开销的堆分配采样方法,可以使用JVMTI 进行访问。

332: Transport Layer Security (TLS) 1.3(支持 TLS 1.3)

http://openjdk.java.net/jeps/332

了解 https的开发者都应该知道TLS,这是 TLS1.3 的实现。

333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental) (可伸缩低延迟垃圾收集器)

http://openjdk.java.net/jeps/333

一个新的低延迟的垃圾回收期,该版本提供的是实验性特性。

335: Deprecate the Nashorn JavaScript Engine(弃用 Nashorn JavaScript 引擎)

http://openjdk.java.net/jeps/335

将 Nashorn javascript 引擎标记为废弃,以后的版本会被删除。世事难料,它是java8才被引入的,这就废弃了,至于原因,规范中说:

With the rapid pace at which ECMAScript language constructs, along with APIs, are adapted and modified, we have found Nashorn challenging to maintain.

大致意思是:随着ECMAScript语言结构和api的快速调整和修改,我们发现很难维护Nashorn。

当版本帝遇到版本帝,一山不容二帝,byebye.

336: Deprecate the Pack200 Tools and API (弃用 Pack200 工具和 API)

http://openjdk.java.net/jeps/336

将pack200 和 unpack200 工具标记为弃用,同时弃用的还有 java.util.jar 中的API。

pack200 是什么?它在java的生命中很重要。pack200 是jar文件的压缩模式,它当初的目标是"在java应用程序打包、传输、交付过程中减少磁盘存储和带宽需求"。

这么重要的工具为什么要废弃,官方给出了三个原因:

  • 过去,由于网络使用mordem拨号的56k 带宽阻碍了java的接受程度。随着java功能的增强,下载数据的大小疯狂增长进一步阻碍了java的接受程度。使用Pack200压缩jdk可以很好的解决这个问题。然而随着技术的发展网速大幅度提升,同时java9提供了jmod工具用以为jre瘦身。java8是最后一个用Pack200打包的正式版本,之后,我们不再需要它了。
  • 除了JDK之外,使用Pack200压缩客户端应用程序(尤其是applet)也很有吸引力。然而随着客户端浏览器UI设计的改变,大部分浏览器不在接受java插件。因此 Pack200的一个主要使用者——applet也不会再帮助它稳固地位。
  • Pack200 是一个复杂的技术,紧紧地与 class文件格式及jar 文件格式相关联。随着JEP 200(模块化jdk)的采用,二者已经发生了不可预料的变化。这个实现将java代码和本地C代码拆分开来,这使它更难维护。Pack200 不利于java se 的模块化。综上所述,维护pack200的成本是巨大的,超过了把它包含在java se和jdk中所带来的好处。
  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值