java 11新特性

之前写了一篇java 8新特性,相信目前很多人还是使用着java 8,那到底java 11值不值得升级呢,结合之前写的java 8与java 11的性能对比,你可以看完这篇java 11的新特性,再考虑要不要升级到java 11。

局部变量类型推断

java 10新出的特性,局部变量类型推断,使用的是var关键字,只能在方法体内使用。

在java 10之前,我们的代码需要这么写:

1
String text = "Hello Java 9";

在java 10后,可以使用var来代替String

1
var text = "Hello Java 10";

这种用法只能使用在局部变量上,即方法体内,不能用在成员变量上:

1
2
3
4
5
6
7
8
public class VarTest {

var v = "123"; //编译错误

public void t1(){
var str = "hello world"; //编译正常
}
}

var说到底其实就是java的语法糖,是靠编译器推断var的类型并转换回去的,并没有改变java静态语言的本质,如:

1
2
var text = "Hello Java 11";
text = 23; // 编译错误,类型改变了,这种写法在动态语言如JavaScript中是可以的,但静态语言的java是不允许的。

想通这一点之后,就可以将var看成只是简化我们代码书写优点,所以使用final关键字,也是可以防止var变量改变的:

1
2
final var text = "Banana";
text = "Joe"; // 编译错误,text声明了final,不可改变

确定了var是语法糖后,对于编译器推断不出来场景,是用不了var的:

1
2
3
4
5
//下面四种都会编译错误
var a;
var nothing = null;
var lambda = () -> System.out.println("Pity!");
var method = this::someMethod;

var对于简化我们的书写还是很有帮助的,对于很长的变量如Map<String, List<Integer>>

1
2
3
4
5
6
var myList = new ArrayList<Map<String, List<Integer>>>();

for (var current : myList) {
// var指向类型: Map<String, List<Integer>>
System.out.println(current);
}

Java 11后,可以将var使用在lambda表达式的参数中:

1
Predicate<String> predicate = (@Nullable var a) -> true;

相信写惯静态语言的程序猿,看动态语言其实是很费劲的,因为不知道这个变量到底是什么类型的,对于var也是一样,好在IDEA可以通过CMD/CTRL指定var可以提示变量类型。

HTTP Client

之前的网络请求,我们一般都是使用Apache的Httpclient工具包,Java 9增加了HttpClient,并在java 11可以在java.net中正式使用。

新的HttpClient可以使用同步或异步的方式请求,并可以使用BodyHandlers来解析响应体,如同步的写法:

1
2
3
4
5
6
7
var request = HttpRequest.newBuilder()
.uri(URI.create("https://www.edjdhbb.com"))
.GET()
.build();
var client = HttpClient.newHttpClient();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());

异步的写法:

1
2
3
4
5
6
7
var request = HttpRequest.newBuilder()
.uri(URI.create("https://www.edjdhbb.com"))
.build(); //不写HTTP方法默认是GET方法
var client = HttpClient.newHttpClient();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);

可以使用BodyPublishers来发送请求内容,类型可以是字符串、字节数组、文件、输入流等:

1
2
3
4
5
6
7
8
var request = HttpRequest.newBuilder()
.uri(URI.create("https://www.edjdhbb.com"))
.header("Content-Type", "text/plain")
.POST(HttpRequest.BodyPublishers.ofString("Hi there!"))//发送字符串内容
.build();
var client = HttpClient.newHttpClient();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode()); // 200

如果服务端的身份验证是HTTP协议的授权认证,那么HttpClient也提供了对应的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
var request = HttpRequest.newBuilder()
.uri(URI.create("https://www.edjdhbb.com"))
.build();
var client = HttpClient.newBuilder()
.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("postman", "password".toCharArray());
}
})//其实是生成了头信息Authorization: Basic $账号密码的basic64编码
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode()); // 200

Collections

java 11里面的集合如ListSetMap都提供了新的创建方法,List.of可以创建新的不可变集合,List.copyOf可以通过拷贝list来创建新的不可变的集合,如:

1
2
3
var list = List.of("A", "B", "C");
var copy = List.copyOf(list);
System.out.println(list == copy); //因为list是不可变的,copyOf方法的目的也是要一个不可变的集合,所以copyOf方法做了个优化,如果输入的集合是不可变的,则直接返回原集合,故这里会打印true
1
2
3
var list = new ArrayList<String>();
var copy = List.copyOf(list);
System.out.println(list == copy); // list是可变的,copyOf则通过list重新创建了一个新的不可变集合,故这里会打印false

通过of方法创建不可变map:

1
2
var map = Map.of("A", 1, "B", 2);
System.out.println(map); // {B=2, A=1}

java 11的不可变集合还是以前的那套不可变实现,熟悉java不可变集合的读者都知道,java原生的不可变集合并非真不可变,只是在原集合上套了一个门面。如:A为可变集合,B为通过A构建的不可变集合,如果只是操作B,那是没问题的,但是操作A就有问题了,A添加了一个元素,B也会添加,因为B只是套在A上的门面。

不过java 11的ofcopyOf方法保证了A不会被操作到,所以问题不大。

Streams

java 8新特性里面笔者已经介绍了一些Streams的方法及用法,也在java 8 stream底层原理里面讲解了它的技术原理,不在重复。在java 11中,streams增加了三个新的方法,Stream.ofNullable类似于Optional可接受一个可能空的实例来构建流:

1
2
Stream.ofNullable(null)
.count() // 打印0

还有dropWhile and takeWhile两个方法,不同于filter方法作用于所有的元素,这两个方法都是遇到第一个false则不在执行while循环:

1
2
3
4
5
6
7
Stream.of(1, 2, 3, 2, 1)
.dropWhile(n -> n < 3) //遇到3则跳出了dropWhile,后面的元素即使小于3也不会被drop
.collect(Collectors.toList()); // [3, 2, 1]

Stream.of(1, 2, 3, 2, 1)
.takeWhile(n -> n < 3) //遇到3则跳出了takeWhile,后面元素即使小于3也不会被take
.collect(Collectors.toList()); // [1, 2]

Optionals

optional增加了转换为stream的方法:

1
Optional.of("foo").stream().count();  // 1

Strings

字符串增加了一些字符操作方法:

1
2
3
4
5
6
" ".isBlank();                // true
" Foo Bar ".strip(); // "Foo Bar"
" Foo Bar ".stripTrailing(); // " Foo Bar"
" Foo Bar ".stripLeading(); // "Foo Bar "
"Java".repeat(3); // "JavaJavaJava"
"A\nB\nC".lines().count(); // 3

InputStreams

InputStream增加了一个非常好用的方法,可以直接传递数据导OutputStream:

1
2
3
4
5
6
var classLoader = ClassLoader.getSystemClassLoader();
var inputStream = classLoader.getResourceAsStream("myFile.txt");
var tempFile = File.createTempFile("myFileCopy", "txt");
try (var outputStream = new FileOutputStream(tempFile)) {
inputStream.transferTo(outputStream);
}

JVM 特性

java 11除了上面那些语言上的API特性,还增加了一些JVM的新特性,可以直接查看官网:

点击了解更多的java知识

  • 本文作者:二当家的
  • 本文链接: 2019/03/09/java-11新特性/
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!
  • 彩蛋: 左边Overview微信公众号二维码,扫描它获取更多技术信息