2.2 Java 9
参考资料
https://www.runoob.com/java/java9-new-features.html
https://www.twle.cn/c/yufei/java9/java9-basic-process-manage-api.html
2.2.1 Jigsaw 模块系统
在 Java 9 以前,打包和依赖都是基于 JAR 包进行的。JRE 中包含了 rt.jar,将近 63M,也就是说要运行一个简单的 Hello World,也需要依赖这么大的 jar 包。在 Java 9 中提出的模块化系统,对这点进行了改善。
关于模块化系统具体可以看看这篇文章。
2.2.2 JShell REPL
Java 9 提供了交互式解释器。有了 JShell 以后,Java 终于可以像 Python,Node.js 一样在 Shell 中运行一些代码并直接得出结果了。
2.2.3. 私有接口方法,接口中使用私有方法
Java 9 中可以在接口中定义私有方法。示例代码如下:
public interface TestInterface { String test(); // 接口默认方法 default String defaultTest() { pmethod(); return "default"; } private String pmethod() { System.out.println("private method in interface"); return "private"; } }
2.2.4. 集合不可变实例工厂方法
在以前,我们想要创建一个不可变的集合,需要先创建一个可变集合,然后使用 unmodifiableSet 创建不可变集合。代码如下:
Set<String> set = new HashSet<>(); set.add("A"); set.add("B"); set.add("C"); set = Collections.unmodifiableSet(set); System.out.println(set);
Java 9 中提供了新的 API 用来创建不可变集合。
List<String> list = List.of("A", "B", "C"); Set<String> set = Set.of("A", "B", "C"); Map<String, String> map = Map.of("KA", "VA", "KB", "VB");
2.2.5. 改进 try-with-resources
Java 9 中不需要在 try 中额外定义一个变量。Java 9 之前需要这样使用 try-with-resources:
InputStream inputStream = new StringBufferInputStream("a"); try (InputStream in = inputStream) { in.read(); } catch (IOException e) { e.printStackTrace(); }
在 Java 9 中可以直接使用 inputStream 变量,不需要再额外定义新的变量了。
InputStream inputStream = new StringBufferInputStream("a"); try (inputStream) { inputStream.read(); } catch (IOException e) { e.printStackTrace(); }
2.2.6. 多版本兼容 jar 包
Java 9 中支持在同一个 JAR 中维护不同版本的 Java 类和资源。
2.2.7. 增强了 Stream,Optional,Process API
2.2.8. 新增 HTTP2 Client
2.2.9. 增强 Javadoc,增加了 HTML 5 文档的输出,并且增加了搜索功能
2.2.10. 增强 @Deprecated
对 Deprecated 新增了 since 和 forRemoval 属性
2.2.11. 增强了钻石操作符 "<>",可以在 匿名内部类中使用了。
在 Java 9 之前,内部匿名类需要指定泛型类型,如下:
1. Handler<? extends Number> intHandler1 = new Handler<Number>(2) { 2. }
而在 Java 9 中,可以自动做类型推导,如下:
1. Handler<? extends Number> intHandler1 = new Handler<>(2) { 2. }
2.2.12. 多分辨率图像 API:定义多分辨率图像API,开发者可以很容易的操作和展示不同分辨率的图像了。
2.2.13. 改进的 CompletableFuture API
CompletableFuture 类的异步机制可以在 ProcessHandle.onExit 方法退出时执行操作。
其他一些改进可参照:
2.3 Java 10
参考资料
https://baijiahao.baidu.com/s?id=1594437679552808575&wfr=spider&for=pc
https://blog.csdn.net/visant/article/details/79778967
https://www.oracle.com/technetwork/java/javase/10-relnote-issues-4108729.html
2.3.1. 局部变量类型推断
局部变量类型推断可以说是Java 10中最值得注意的特性,这是Java语言开发人员为了简化Java应用程序的编写而采取的又一步,如下图所示。
这个新功能将为Java增加一些语法糖 - 简化它并改善开发者体验。新的语法将减少与编写Java相关的冗长度,同时保持对静态类型安全性的承诺。
局部变量类型推断将引入"var"关键字,也就是你可以随意定义变量而不必指定变量的类型,如:
将被下面这个新语法所取代:
看完是不是有点JS的即视感???越来越像JS了吗?!虽然类型推断在Java中不是一个新概念,但在局部变量中确是很大的一个改进。
说到类型推断,从JDK 5引进泛型,到JDK 7的"<>"操作符允许不绑定类型而初始化List,再到JDK 8的Lambda表达式,再到现在JDK 10的局部变量类型推断,Java类型推断正大刀阔斧的向前发展。
局部变量类型推荐仅限于如下使用场景:
- 局部变量初始化
- for循环内部索引变量
- 传统的for循环声明变量
Java官方表示,它不能用于以下几个地方:
- 方法参数
- 构造函数参数
- 方法返回类型
- 字段捕获表达式(或任何其他类型的变量声明)
2.3.2. GC改进和内存管理
JDK 10中有2个JEP专门用于改进当前的垃圾收集元素。
第一个垃圾收集器接口是(JEP 304),它将引入一个纯净的垃圾收集器接口,以帮助改进不同垃圾收集器的源代码隔离。
预定用于Java 10的第二个JEP是针对G1的并行完全GC(JEP 307),其重点在于通过完全GC并行来改善G1最坏情况的等待时间。G1是Java 9中的默认GC,并且此JEP的目标是使G1平行。
2.3.3. 线程本地握手(JEP 312)
JDK 10将引入一种在线程上执行回调的新方法,因此这将会很方便能停止单个线程而不是停止全部线程或者一个都不停。
2.3.4. 备用内存设备上的堆分配(JEP 316)
允许HotSpot VM在备用内存设备上分配Java对象堆内存,该内存设备将由用户指定。
2.3.5. 其他Unicode语言 - 标记扩展(JEP 314)
目标是增强java.util.Locale及其相关的API,以便实现语言标记语法的其他Unicode扩展(BCP 47)。
2.3.6. 基于Java的实验性JIT编译器
Oracle希望将其Java JIT编译器Graal用作Linux / x64平台上的实验性JIT编译器。
2.3.7. 根证书(JEP 319)
这个的目标是在Oracle的Java SE中开源根证书。
2.3.8. 根证书颁发认证
这将使OpenJDK对开发人员更具吸引力,它还旨在减少OpenJDK和Oracle JDK构建之间的差异。
2.3.9. 将JDK生态整合单个存储库(JEP 296)
此JEP的主要目标是执行一些内存管理,并将JDK生态的众多存储库组合到一个存储库中。
2.3.10. 删除工具javah(JEP 313)
从JDK中移除了javah工具,这个很简单并且很重要。
其他可参考:
https://www.oracle.com/technetwork/java/javase/10-relnote-issues-4108729.html#NewFeature
2.4 Java 11
参考资料
https://blog.csdn.net/weixin_38055381/article/details/82865385
2.4.1 本地变量类型推断
什么是局部变量类型推断?
var javastack = "javastack";
System.out.println(javastack);
大家看出来了,局部变量类型推断就是左边的类型直接使用 var 定义,而不用写具体的类型,编译器能根据右边的表达式自动推断类型,如上面的 String 。
var javastack = "javastack";
就等于:
String javastack = "javastack";
2.4.2 字符串加强
Java 11 增加了一系列的字符串处理方法,如以下所示。
// 判断字符串是否为空白
" ".isBlank(); // true
// 去除首尾空格
" Javastack ".strip(); // "Javastack"
// 去除尾部空格
" Javastack ".stripTrailing(); // " Javastack"
// 去除首部空格
" Javastack ".stripLeading(); // "Javastack "
// 复制字符串
"Java".repeat(3);// "JavaJavaJava"
// 行数统计
"A\nB\nC".lines().count(); // 3
2.4.3 集合加强
自 Java 9 开始,Jdk 里面为集合(List/ Set/ Map)都添加了 of 和 copyOf 方法,它们两个都用来创建不可变的集合,来看下它们的使用和区别。
示例1:
var list = List.of("Java", "Python", "C"); var copy = List.copyOf(list); System.out.println(list == copy); // true
示例2:
1. var list = new ArrayList<String>(); 2. var copy = List.copyOf(list); 3. System.out.println(list == copy); // false
示例1和2代码差不多,为什么一个为true,一个为false?
来看下它们的源码:
static <E> List<E> of(E... elements) { switch (elements.length) { // implicit null check of elements case 0: return ImmutableCollections.emptyList(); case 1: return new ImmutableCollections.List12<>(elements[0]); case 2: return new ImmutableCollections.List12<>(elements[0], elements[1]); default: return new ImmutableCollections.ListN<>(elements); } } static <E> List<E> copyOf(Collection<? extends E> coll) { return ImmutableCollections.listCopy(coll); } static <E> List<E> listCopy(Collection<? extends E> coll) { if (coll instanceof AbstractImmutableList && coll.getClass() != SubList.class) { return (List<E>)coll; } else { return (List<E>)List.of(coll.toArray()); } }
可以看出 copyOf 方法会先判断来源集合是不是 AbstractImmutableList 类型的,如果是,就直接返回,如果不是,则调用 of 创建一个新的集合。
示例2 因为用的 new 创建的集合,不属于不可变 AbstractImmutableList 类的子类,所以 copyOf 方法又创建了一个新的实例,所以为false.
注意:使用of和copyOf创建的集合为不可变集合,不能进行添加、删除、替换、排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。
上面演示了 List 的 of 和 copyOf 方法,Set 和 Map 接口都有。
2.4.4 Stream 加强
Stream 是 Java 8 中的新特性,Java 9 开始对 Stream 增加了以下 4 个新方法。
1) 增加单个参数构造方法,可为null
Stream.ofNullable(null).count(); // 0
2) 增加 takeWhile 和 dropWhile 方法
1. Stream.of(1, 2, 3, 2, 1) 2. .takeWhile(n -> n < 3) 3. .collect(Collectors.toList()); // [1, 2]
从开始计算,当 n < 3 时就截止。
1. Stream.of(1, 2, 3, 2, 1) 2. .dropWhile(n -> n < 3) 3. .collect(Collectors.toList()); // [3, 2, 1]
这个和上面的相反,一旦 n < 3 不成立就开始计算。
3)iterate重载
这个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。
如果你对 JDK 8 中的 Stream 还不熟悉,可以看之前分享的这一系列教程。
2.4.5 Optional 加强
Opthonal 也增加了几个非常酷的方法,现在可以很方便的将一个 Optional 转换成一个 Stream, 或者当一个空 Optional 时给它一个替代的。
Optional.of("javastack").orElseThrow(); // javastack Optional.of("javastack").stream().count(); // 1 Optional.ofNullable(null) .or(() -> Optional.of("javastack")) .get(); // javastack
2.4.6 InputStream 加强
InputStream 终于有了一个非常有用的方法:transferTo,可以用来将数据直接传输到 OutputStream,这是在处理原始数据流时非常常见的一种用法,如下示例。
var classLoader = ClassLoader.getSystemClassLoader(); var inputStream = classLoader.getResourceAsStream("javastack.txt"); var javastack = File.createTempFile("javastack2", "txt"); try (var outputStream = new FileOutputStream(javastack)) { inputStream.transferTo(outputStream); }
2.4.7 HTTP Client API
这是 Java 9 开始引入的一个处理 HTTP 请求的的孵化 HTTP Client API,该 API 支持同步和异步,而在 Java 11 中已经为正式可用状态,你可以在 java.net 包中找到这个 API。
来看一下 HTTP Client 的用法:
var request = HttpRequest.newBuilder() .uri(URI.create("https://javastack.cn")) .GET() .build(); var client = HttpClient.newHttpClient(); // 同步 HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); // 异步 client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) .thenApply(HttpResponse::body) .thenAccept(System.out::println);
上面的 .GET() 可以省略,默认请求方式为 Get!
更多使用示例可以看这个 API,后续有机会再做演示。
现在 Java 自带了这个 HTTP Client API,我们以后还有必要用 Apache 的 HttpClient 工具包吗?
2.4.8 化繁为简,一个命令编译运行源代码
看下面的代码。
// 编译
javac Javastack.java
// 运行
java Javastack
在我们的认知里面,要运行一个 Java 源代码必须先编译,再运行,两步执行动作。而在未来的 Java 11 版本中,通过一个 java 命令就直接搞定了,如以下所示。
java Javastack.java
其他一些改进可以参照:
https://www.oracle.com/technetwork/java/javase/11-relnote-issues-5012449.html#NewFeature
2.5 Java 12
参考资料
2.5.1 switch 表达式
Java 12 以后,switch 不仅可以作为语句,也可以作为表达式。
private String switchTest(int i) { return switch (i) { case 1 -> "1"; default -> "0"; }; }
2.5.2 2 默认CDS归档
通过在64位平台上的默认类列表的帮助下生成CDS归档来改进JDK构建过程,从而有效地消除了运行java -Xshare:dump。 此功能的目标包括:
- 改进开箱即用的启动时间
- 摆脱使用-Xshare:dump。
2.5.3 Shenandoah GC
Shenandoah是一种垃圾收集(GC)算法,旨在保证低延迟(10 - 500 ms的下限)。 它通过在运行Java工作线程的同时执行GC操作减少GC暂停时间。 使用Shenandoah,暂停时间不依赖于堆的大小。 这意味着无论堆的大小如何,暂停时间都是差不多的。
这是一个实验性功能,不包含在默认(Oracle)的OpenJDK版本中。
2.5.4 JMH 基准测试
此功能为JDK源代码添加了一套微基准测试(大约100个),简化了现有微基准测试的运行和新基准测试的创建过程。 它基于Java Microbenchmark Harness(JMH)并支持JMH更新。
此功能使开发人员可以轻松运行当前的微基准测试并为JDK源代码添加新的微基准测试。 可以基于Java Microbenchmark Harness(JMH)轻松测试JDK性能。 它将支持JMH更新,并在套件中包含一组(约100个)基准测试。
2.5.5 JVM 常量 API
JEP 334引入了一个API,用于建模关键类文件和运行时artifacts,例如常量池。 此API将包括ClassDesc,MethodTypeDesc,MethodHandleDesc和DynamicConstantDesc等类。此 API 对于操作类和方法的工具很有帮助。
2.5.6 G1的可中断 mixed GC
此功能通过将Mixed GC集拆分为强制部分和可选部分,使G1垃圾收集器更有效地中止垃圾收集过程。通过允许垃圾收集过程优先处理强制集,g1可以更多满足满足暂停时间目标。
G1是一个垃圾收集器,设计用于具有大量内存的多处理器机器。由于它提高了性能效率,g1垃圾收集器最终将取代cms垃圾收集器。
G1垃圾收集器的主要目标之一是满足用户设置的暂停时间。G1采用一个分析引擎来选择在收集期间要处理的工作量。此选择过程的结果是一组称为GC集的区域。一旦GC集建立并且GC已经开始,那么G1就无法停止。
如果G1发现GC集选择选择了错误的区域,它会将GC区域的拆分为两部分(强制部分和可选部分)来切换到处理Mix GC的增量模式。如果未达到暂停时间目标,则停止对可选部分的垃圾收集。
2.5.7 G1归还不使用的内存
此功能的主要目标是改进G1垃圾收集器,以便在不活动时将Java堆内存归还给操作系统。 为实现此目标,G1将在低应用程序活动期间定期生成或持续循环检查完整的Java堆使用情况。
这将立即归还未使用的部分Java堆内存给操作系统。 用户可以选择执行FULL GC以最大化返回的内存量。
2.5.8移除多余ARM64实现
Java 12将只有一个ARM 64位实现(aarch64)。 目标是删除所有与arm64实现相关的代码,同时保留32位ARM端口和64位aarch64实现。
这将把重点转移到单个64位ARM实现,并消除维护两个实现所需的重复工作。 当前的JDK 11实现中有两个64位ARM实现。
其他一些改进可以参照:
https://www.oracle.com/technetwork/java/javase/12-relnote-issues-5211422.html#NewFeature
三、其他参考资料
https://gudong.name/2019/04/05/android-why-java-harmony.html
https://juejin.im/post/5ca1c747e51d45761c7441fa
https://www.zhihu.com/question/19646618
www.zh.wikipedia.org/wiki/Java版本歷史
https://www.baeldung.com/oracle-jdk-vs-openjdk