就在前段时间,Oracle 官方宣布 Java 11 (18.9 LTS) 正式发布,可在生产环境中使用! 这无疑对我们来说是一大好的消息。作为一名java开发者来说,虽然又要去学习和了解java11,但内心还是欣慰的。我想至少你和我一样的心情:Java在手,天下我有!
今天我们来看一下Java 11到底是什么、他有什么特别的、到底要不要升级到Java 11。
在Oracle官网中,进入下载页面,第一个可供下载的JDK版本已经提换成了Java SE 11 (LTS),这里的LTS表示Long-Term-Support。
本次发布的Java 11和2017年9月份发布的Java 9以及 2018年3月份发布的Java 10相比,其最大的区别就是:在长期支持(Long-Term-Support)方面,Oracle表示会对Java 11提供大力支持,这一支持将会持续至2026年9月。这是据 Java 8 以后支持的首个长期版本。
为什么说是长期版本,看下面的官方发布的支持路线图表。
上图是一张Oracle 公布的对于各个版本的JDK的Support Roadmap。途中列举了Java 6 - Java 12的正式发布时间以及支持计划。
从中可以看出,在Java 11之前,Java 9和Java 10是不提供长期支持的,而上一个提供长期支持的版本是Java 8,其将会支持到2025年3月。
长期支持,表示Oracle会对其做长期的补丁、安全等扩展支持等。
下一个提供长期支持的版本将会是Java 17,其将于2021年发布。
现在大部分都在用 Java 8,Java 9 和 10 目前很少有人在用,至少我没有发现有公司在生产环境应用的,那就是找死。
现在 Java 11 长期支持,也已经包含了 9 和 10 的全部功能,9 和 10 自然就活到头了。。
那么我们来看下 从 Java 9 - 11 都有哪些重要的新特性呢?
什么是局部变量类型推断?
var javastack = "javastack"; System.out.println(javastack); 复制代码
大家看出来了,局部变量类型推断就是左边的类型直接使用 var 定义,而不用写具体的类型,编译器能根据右边的表达式自动推断类型,如上面的 String 。
var javastack = "javastack"; 复制代码
就等于:
String javastack = "javastack"; 复制代码
Java 11 增加了一系列的字符串处理方法,如以下所示。
// 判断字符串是否为空白 " ".isBlank(); // true // 去除首尾空格 " Javastack ".strip(); // "Javastack" // 去除尾部空格 " Javastack ".stripTrailing(); // " Javastack" // 去除首部空格 " Javastack ".stripLeading(); // "Javastack " // 复制字符串 "Java".repeat(3); // "JavaJavaJava" // 行数统计 "A/nB/nC".lines().count(); // 3 复制代码
自 Java 9 开始,Jdk 里面为集合(List/ Set/ Map)都添加了 of 和 copyOf 方法,它们两个都用来创建不可变的集合,来看下它们的使用和区别。
var list = List.of("Java", "Python", "C"); var copy = List.copyOf(list); System.out.println(list == copy); // true 复制代码
var list = new ArrayList<String>(); var copy = List.copyOf(list); 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 接口都有。
Stream 是 Java 8 中的新特性,Java 9 开始对 Stream 增加了以下 4 个新方法。
Stream.ofNullable(null).count(); // 0 复制代码
Stream.of(1, 2, 3, 2, 1) .takeWhile(n -> n < 3) .collect(Collectors.toList()); // [1, 2] 复制代码
从开始计算,当 n < 3 时就截止。
Stream.of(1, 2, 3, 2, 1) .dropWhile(n -> n < 3) .collect(Collectors.toList()); // [3, 2, 1] 复制代码
这个和上面的相反,一旦 n < 3 不成立就开始计算。
3)iterate重载
这个 iterate 方法的新重载方法,可以让你提供一个 Predicate (判断条件)来指定什么时候结束迭代。
如果你对 JDK 8 中的 Stream 还不熟悉,可以看之前分享的这一系列教程。
Opthonal 也增加了几个非常酷的方法,现在可以很方便的将一个 Optional 转换成一个 Stream, 或者当一个空 Optional 时给它一个替代的。
Optional.of("javastack").orElseThrow(); // javastack Optional.of("javastack").stream().count(); // 1 Optional.ofNullable(null) .or(() -> Optional.of("javastack")) .get(); // javastack 复制代码
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); } 复制代码
这是 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 工具包吗?
看下面的代码。
// 编译 javac Javastack.java // 运行 java Javastack 复制代码
在我们的认知里面,要运行一个 Java 源代码必须先编译,再运行,两步执行动作。而在未来的 Java 11 版本中,通过一个 java 命令就直接搞定了,如以下所示。
java Javastack.java 复制代码
新发布的Java 11在新特性方面,提供了17个JEP(JDK Enhancement Proposal 特性增强提议)
上图是Oracle公布的Java 11包含的所有新特性,其中几个重点的新特性为:
ZGC是一款号称可以保证每次GC的停顿时间不超过10MS的垃圾回收器,并且和当前的默认垃圾回收起G1相比,吞吐量下降不超过15%。
Java 11还加入了一个比较特殊的垃圾回收器——Epsilon,该垃圾收集器被称为“no-op”收集器,将处理内存分配而不实施任何实际的内存回收机制。 也就是说,这是一款不做垃圾回收的垃圾回收器。这个垃圾回收器看起来并没什么用,主要可以用来进行性能测试、内存压力测试等,Epsilon GC可以作为度量其他垃圾回收器性能的对照组。大神Martijn说,Epsilon GC至少能够帮助理解GC的接口,有助于成就一个更加模块化的JVM。
Java 10中增加了本地变量类型推断的特性,可以使用var来定义局部变量。尽管这一特性被很多人诟病,但是并不影响Java继续增强他的用法,在Java 11中,var可以用来作为Lambda表达式的局部变量声明。
早在发布Java SE 9的时候,Java就表示过,会在未来版本中将Java EE和CORBA模块移除,而这样举动终于在Java 11中实施。终于去除了Java EE和CORBA模块。
JDK 9 中就已对 HTTP Client API 进行标准化,然后通过JEP 110,在 JDK 10 中进行了更新。在本次的Java 11的更新列表中,由以JEP 321进行进一步升级。该API通过CompleteableFutures提供非阻塞请求和响应语义,可以联合使用以触发相应的动作。 JDK 11完全重写了该功能。现在,在用户层请求发布者和响应发布者与底层套接字之间追踪数据流更容易了,这降低了复杂性,并最大程度上提高了HTTP / 1和HTTP / 2之间的重用的可能性。
2017年8月,JCP执行委员会提出将Java的发布频率改为每六个月一次。
大部分人使用的JDK版本还是Java 8及以下版本,甚至某些公司的生产环境使用的还是JDK 1.6。
那么,对于公司和开发者来说,到底要不要在生产及开发环境中升级和学习Java 11呢?
对于企业来说,生产环境中的JDK版本升级到Java 11还是有必要的。主要有两个原因:
1、Oracle会对Java 11提供长期支持,企业可以放心使用这一版本。并且下一个长期支持的版本会在三年后发布,时间比较久远。
2、Java 11确实提供了一些比较不错的特性,尤其重要的是提供了ZGC,这是一款具有划时代意义的垃圾回收器。优点不再赘述。有了ZGC,JVM的性能瓶颈可以被突破。
在编码方面,Java 11并没有像Java 8那样变化巨大,毕竟Java 8提供了函数式编程的能力,这也是很多开发者学习Java 8的一个重要原因。
但是,Java 11也并不是完全没有提升,至少在新版本中,Java开发者终于可以摆脱老旧的HttpURLConnection了。新的HTTP API提供了对HTTOP/2等业界前沿标准的支持,提供了精简而又友好的API接口。
所以,综上所述,无论是对于企业还是开发者来说,升级Java 11都是有必要的,至少比Java 9和Java 10的必要性要大很多。至于这个必要性到底有多大呢,作者给一个简单的说明: