在去年的9月26日,Oracle官方宣布Java11正式发布,这是Java大版本周期变化后的第一个长期支持版本,很是值得关注。Java9和Java10都在很短的时间内就过渡了,因此,Java11将是一个不可忽视的版本。从时间节点看,JDK11的发布正好处在JDK8免费更新到期的前夕,同时,JDK八、9也将陆续成为"历史版本"。javascript
那么,关于Java11的新特性到底有哪些呢?容我一一介绍。php
局部类型推断
什么是局部类型推断?css
var str = "helloworld";System.out.println(str);
局部变量类型推断就是左边的类型直接使用var定义,而不用写具体的类型,编译器能根据右边的表达式自动推断类型,如上面的str变量使用var定义,编译器就能经过右边的"helloworld"自动推断出这是一个String类型的变量。java
可是,值得注意的是,这个var并非一个关键字,不少同窗看到变量都能使用var来定义,那var还不是关键字吗?事实上,它真的不是一个关键字。nginx
int var = 10;System.out.println(var);
例如上面的这段代码是可以正确运行的,这证实var不是关键字。
咱们还能够经过反编译来看,例如咱们反编译这样一段代码:typescript
var a = 100;System.out.println(a);
反编译获得的结果为:shell
byte a = 100; System.out.println(a);
从这里能够看出,var仅仅是一个语法上的改进,在编译时期便已经将var转换为了对应的变量类型。
然而在使用var定义变量时,必须马上赋值,例以下面的状况是错误的:bash
var a;
由于在不赋值的状况下,JVM没法推断当前变量的类型。
在类中的成员变量(也叫属性)不可使用var来定义,例以下面的状况是错误的:微信
class Student{ var name = "小明"; var age = 20;}
var的好处在lambda表达式中体现得淋漓尽致。咱们知道,开启一个线程可使用lambda表达式来完成:并发
Thread t = new Thread(() -> System.out.println(Thread.currentThread().getName()));t.start();
这是一个无参的lambda表达式形式,咱们再看一个带参lambda表达式:
String[] arr = { "program", "creek", "is", "a", "java", "site" };Stream<String> stream = Stream.of(arr);stream.forEach(x -> System.out.print(x + "\t"));
这是一个forEach的用法,其中须要用到变量x,由于这里它自动推断出了x的类型为String,因此String被省略了,那么加上var以后代码变成这样:
String[] arr = { "program", "creek", "is", "a", "java", "site" };Stream<String> stream = Stream.of(arr);stream.forEach((var x) -> System.out.print(x + "\t"));
若是仅仅只是这样写,却是没法看出写var有什么优点,反而以为有点画蛇添足,可是若是要给lambda表达式变量标注注解的话,那么这个时候var的做用就体现出来了。
String[] arr = { "program", "creek", "is", "a", "java", "site" };Stream<String> stream = Stream.of(arr);stream.forEach((var x) -> System.out.print(x + "\t"));
可是,咱们从何得知x的类型呢?其实咱们不用知晓,由于var就能自动推断,因此,var的好处在这里就体现出来了。
集合中的新API
在Java9以前,咱们要想建立新集合,咱们得这样作:
List<String> list = new ArrayList<String>();list.add("hello");list.add("world");list.add("java");
建立过程略显麻烦,那么如今,咱们能够经过这样的方式来建立集合:
List<String> list = List.of("hello","world","java");
可是,请注意了,用这样的方式来建立的集合,是没法添加元素的,咱们能够尝试着添加一下:
List<String> list = List.of("hello","world","java");list.add("test");
运行结果以下:
Exception in thread "main" java.lang.UnsupportedOperationException at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:71) at java.base/java.util.ImmutableCollections$AbstractImmutableCollection.add(ImmutableCollections.java:75) at com.itcast.TestDemo.main(TestDemo.java:8)
那么这究竟是为何呢?咱们经过源码来分析一下。
首先我么看看List类的of()方法:
static <E> List<E> of(E e1, E e2, E e3) { return new ImmutableCollections.ListN<>(e1, e2, e3);}
该方法调用了ImmutableCollections类的ListN()生成一个集合并返回,咱们看看ListN的源码:
static final class ListN<E> extends AbstractImmutableList<E> implements Serializable {
static final List<?> EMPTY_LIST = new ListN<>();
private final E[] elements;
ListN(E... input) { // copy and check manually to avoid TOCTOU "unchecked") ( E[] tmp = (E[])new Object[input.length]; // implicit nullcheck of input for (int i = 0; i < input.length; i++) { tmp[i] = Objects.requireNonNull(input[i]); } elements = tmp; }
public boolean isEmpty() { return size() == 0; }
public int size() { return elements.length; }
public E get(int index) { return elements[index]; }
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { throw new InvalidObjectException("not serial proxy"); }
private Object writeReplace() { return new CollSer(CollSer.IMM_LIST, elements); } }
它是ImmutableCollections类的一个静态内部类,咱们暂且无论它是如何生成集合的,咱们找找里面有没有add()方法,会发现里面并不存在add()方法,那么咱们既然可以调用到,那么add()方法确定在其父类中。最终,在它的父类AbstractImmutableCollection中找到了add()方法:
// all mutating methods throw UnsupportedOperationException public boolean add(E e) { throw uoe(); } public boolean addAll(Collection<? extends E> c) { throw uoe(); } public void clear() { throw uoe(); } public boolean remove(Object o) { throw uoe(); } public boolean removeAll(Collection<?> c) { throw uoe(); } public boolean removeIf(Predicate<? super E> filter) { throw uoe(); } public boolean retainAll(Collection<?> c) { throw uoe(); }
add()方法调用了uoe()方法,而uoe()方法直接抛出了一个异常:
static UnsupportedOperationException uoe() { return new UnsupportedOperationException(); }
会发现,调用uoe()方法的不仅add()方法一个,有关于集合添加、修改、删除的种种操做都会抛出异常。因此,由of()方法建立的集合是不能够进行这些相关操做的。
流中的新API
上面集合中说到的of()方法一样能够用在流中。
Stream<Integer> stream = Stream.of(1,2,3,4,5);
Stream stream = Stream.of();Stream stream2 = Stream.of(null);
而在上面的两条语句中,第二条语句会产生空指针异常,固然,咱们不能容许咱们的程序出现这样的异常,可是你又颇有可能会传入一个null,这样的状况该如何避免呢?从Java9开始,出现了一个新方法:
Stream stream3 = Stream.ofNullable(null);
该方法容许你传入一个null值,以此避免空指针异常产生。
继续介绍Stream中的新API。
1.takeWhile
该方法会从流中一直获取断定器为真的元素,一旦遇到元素为假,就终止处理
Stream<Integer> stream = Stream.of(1, 3, 2, 5, 6, 7);Stream stream2 = stream.takeWhile(t -> t % 2 != 0);stream2.forEach(System.out::println);
这段程序的运行结果:
13
你如果理解了这个方法的意思,这样的输出结果就不难理解。由于当获取到元素2时,断定器为假,此时会终止处理,因此后面的元素就不会再去处理。
2.dropWhile()
那么这方法和takeWhile()方法相反,它会从流中一直丢弃断定器为真的元素,一旦遇到元素为假,就终止处理
Stream<Integer> stream = Stream.of(1, 3, 2, 5, 6, 7);Stream stream2 = stream.dropWhile(t -> t % 2 != 0);stream2.forEach(System.out::println);
因此上面程序段的执行结果为:
2567
字符串中的新API
1.isBlank()
判断字符串中的字符是否都为空白
2.strip()
去除字符串首尾的空白
3.stripTrailing()
去除字符串尾部的空白
4.stripLeading()
去除字符串首部的空白
5.repeat()
复制字符串,能够传入一个int类型值来控制复制次数
咱们知道在字符串处理方法中,trim()方法也可以去除字符串首尾的空白,那为何Oracle还要设计一个重复的方法呢?这必然有它的道理。其实,trim()方法要比strip()方法简单得多:
/** * Returns a string whose value is this string, with all leading * and trailing space removed, where space is defined * as any character whose codepoint is less than or equal to * {@code 'U+0020'} (the space character). */ public String trim() { String ret = isLatin1() ? StringLatin1.trim(value) : StringUTF16.trim(value); return ret == null ? this : ret; }
经过查阅源码中对该方法的注释发现,trim()方法只能去除Unicode码值小于等于32的空白字符,而32正好指的是空格,那么对于全角的空格,trim()方法就无能为力了。因此在功能上,strip()方法更增强大。
HttpAPI
这是Java9开始引入的一个处理HTTP请求的API,该API支持同步和异步,而在Java11中已经为正式可用状态。
HttpClient client = HttpClient.newHttpClient();HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.baidu.com")).build();BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();HttpResponse<String> response = client.send(request, responseBodyHandler);String body = response.body();System.out.println(body);
这是一段基本的访问百度的请求代码,固然,它还提供了异步请求方式:
HttpClient client = HttpClient.newHttpClient();HttpRequest request = HttpRequest.newBuilder(URI.create("http://www.baidu.com")).build();BodyHandler<String> responseBodyHandler = BodyHandlers.ofString();CompletableFuture<HttpResponse<String>> sendAsync = client.sendAsync(request, responseBodyHandler);HttpResponse<String> response = sendAsync.get();String body = response.body();System.out.println(body);
新版本废弃了哪些内容
删除了com.sun.awt.AWTUtilities类
从Oracle JDK中删除了Lucida字体
Oracle JDK再也不提供任何字体,彻底依赖于操做系统上安装的字体。删除了appletviewer启动器
Epsilon垃圾收集器
JDK上对这个特性的描述是:开发一个处理内存分配但不实现任何实际内存回收机制的GC,一旦可用堆内存用完,JVM就会退出。
咱们能够来尝试着使用一下它,首先咱们编写一段程序:
public class EpsilonTest { public static void main(String[] args) throws Exception { var list = new ArrayList<>(); boolean flag = true; int count = 0; while (flag) { list.add(new Garbage()); if (count++ == 500) { list.clear(); } } }}
class Garbage { private double d1 = 1; private double d2 = 2;
/** * GC在清除本对象时会调用该方法 */ protected void finalize() throws Throwable { System.out.println(this + " collecting"); }}
这是一个无限循环的程序,循环体不断建立Garbage对象并放入集合,当循环次数达到500时将集合清空,此时的500个对象均为垃圾,会被GC清理,清理时调用finalize()方法打印信息。运行这段程序,结果以下:
...com.itcast.Garbage@1e9c634c collectingjava.lang.OutOfMemoryError: Java heap space at com.itcast.EpsilonTest.main(EpsilonTest.java:11)com.itcast.Garbage@1174213e collectingcom.itcast.Garbage@2029a4b8 collecting...
当程序执行到某一刻时,内存溢出,程序终止。
如今咱们来使用一下Epsilon,右键选择类文件,在Run As右侧选择Run Configurations:
如今咱们将默认的GC换为了Epsilon,再来看看运行结果:
Terminating due to java.lang.OutOfMemoryError: Java heap space
会发现,控制台只输出了这么一句,说明被清除的集合中的对象并无被回收,并且内存溢出的速度也很是快,这说明该GC是并不会回收垃圾,那么它有什么做用呢?
它提供彻底被动的GC实现,具备有限的分配限制和尽量低的延迟开销,但代价是内存占用和内存吞吐量,它的主要用途有如下几个方面:
性能测试(它能够帮助过滤掉GC引发的性能假象)
内存压力测试
很是短的JOB任务
VM接口测试
ZGC垃圾回收器
有人说这是JDK11最为瞩目的特性,没有之一,是最重磅的升级,那么ZGC的优点在哪里呢?
GC暂停时间不会超过10毫秒
既能处理几百兆的小堆,也能处理几个T的大堆
和G1相比,应用吞吐能力不会降低超过15%
为将来的GC功能和利用colord指针以及Load barriers优化奠基了基础
ZGC是一个并发、基于region、压缩型的垃圾收集器,只有root扫描阶段会STW(stop the world,中止全部线程),所以ZGC的停顿时间不会随着堆的增加和存活对象的增加而变长。
用法:-XX:UnlockExperimentalVMOptions -XX:+UseZGC
虽然功能如此强大,但很遗憾的是,在Windows系统的JDK中并无提供ZGC,因此也就没有办法尝鲜了。
Flight Recorder
这是一个记录仪,用于诊断程序运行过程,那么在以前这是一个商业版的特性,是要收费的,从Java11开始,Fight Recorder免费提供使用并开源。它能够导出事件到文件中,以后能够用Java Mission Control来分析,也能够在应用启动时配置java -XX:StartFlightRecording或者在应用启动以后使用jcmd来录制,好比:
jcmd <pid> JFR.start jcmd <pid> JFR.dump filename= jcmd <pid> JFR.stop
其它
在Java11中,支持一个命令编译运行文件,在以前的版本中,咱们要想运行一个Java程序,首先得用javac指令编译,而后用java指令运行。而在新版本中,咱们直接使用java指令便可完成编译运行操做。
在Unicode10版本中,增长了8518个字符,总计达到了136690个字符,这已经超出了char类型的数值范围,因此在Java11中,新增了CharacterData,使用四个字节来处理字符。
那么有关Java11的新特性就介绍到这里。
本文分享自微信公众号 - 码视界(otc_18679428729)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。