Java 11 教程
Java11 已于 2018/09/25 成功发布,不过目前 绝大多数人 在生产环境仍旧使用的是Java 8。这篇以案例为主的教程涵盖了从 Java 9 到 Java 11的绝大多数重要的语法与API特性。让我们开始吧!
局部变量类型推断
Java 10引入了一个新的语言关键字var
,它可以在声明局部变量 时替换类型信息( 局部 意味着方法体内的变量声明)。
Java 10之前,变量的声明形式如下:
String text = "Hello Java 9";
现在,你可以使用 var
替换 String
。编译器将会从变量的赋值中推断出它的正确类型。在这个例子里 变量text
即为 String
类型:
var text = "Hello Java 10";
不同于 Javascript 中的 var
关键字,Java中的 var
声明的变量仍旧是静态类型。你不能再次赋予另一个与原类型不符的变量值。
var text = "Hello Java 11"; text = 23; // ERROR: Incompatible types(类型错误)
var
关键字还可以与 final
一起使用,意义同之前的版本一样,表示不可修改。
final var text = "Hello Java 10"; text = "Hello Java 11"; // Cannot assign a value to final variable 'text'
当编译器不能正确识别出变量的数值类型时,var
将不被允许使用。下面这些代码都是没法编译的代码:
// Cannot infer type: var a; var nothing = null; var lambda = () -> System.out.println("Pity!"); var method = this::someMethod;
局部变量类型推断在与复杂的泛型类型结合时,能放大它的价值。在下面这个例子中,current
是有着一个冗长的数据类型 Map<String, List<Integer>>
,不过它的类型声明可以被 var
这个关键字简单地替换掉,让你避免了写一大窜的类型麻烦事。
var myList = new ArrayList<Map<String, List<Integer>>>(); for (var current : myList) { // current is infered to type: Map<String, List<Integer>> System.out.println(current); }
从Java 11开始,lambda表达式的参数也允许使用var关键字,这样使得你可以为这些参数添加注解标识:
Predicate<String> predicate = (@Nullable var a) -> true;
Tip:在Intellij IDEA中,你可以在按住CMD / CTRL的同时将鼠标悬停在变量上,以显示变量的推断类型。
HTTP Client
Java 9引入了一个新的孵化HttpClient
API来处理HTTP请求。从Java 11开始,这个API已经可以在标准库 java.net
中使用了。让我们来探索一下通过这个API我们可以做些什么。
这个新的 HttpClient
既可以被同步使用,也可以被异步使用。同步请求将会阻塞当前的线程,直到返回响应消息。BodyHandlers
定义了响应消息体的类型(e.g string,byte-array 或 file):
var request = HttpRequest.newBuilder() .uri(URI.create("https://wangwei.one")) .GET() .build(); var client = HttpClient.newHttpClient(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body());
同样的请求也可以被异步执行。调用 sendAsync
方法不会阻塞当前线程,并且会返回 CompletableFuture
对象,用来构建异步执行结果的操作流。
var request = HttpRequest.newBuilder() .uri(URI.create("https://wangwei.one")) .build(); var client = HttpClient.newHttpClient(); client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) .thenApply(HttpResponse::body) .thenAccept(System.out::println);
我们可以省略
.GET()
的调用,因为它默认的请求方式。
下面这个列子,我们以POST
的方法向指定的URL发送数据。类似于 BodyHandlers
,你可以使用 BodyPublishers
去定义请求消息体中你想要发送的数据类型,例如 strings, byte-arrays,files 或 input-streams:
var request = HttpRequest.newBuilder() .uri(URI.create("https://postman-echo.com/post")) .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
最后这个列子来演示如何通过 BASIC-AUTH
来执行身份认证。
var request = HttpRequest.newBuilder() .uri(URI.create("https://postman-echo.com/basic-auth")) .build(); var client = HttpClient.newBuilder() .authenticator(new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("postman", "password".toCharArray()); } }) .build(); var response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.statusCode()); // 200
Collections
像List
, Set
和 Map
这样的集合,它们的API也得到了扩展。List.of
可以从给定的参数中创建一个不可变的list。List.copyOf
可以创建一个 list 的不可变副本。
var list = List.of("A", "B", "C"); var copy = List.copyOf(list); System.out.println(list == copy); // true
因为 list
已经是不可变的,所以实际上不需要实际地地去创建 list
实例的副本,因此 list
和 copy
是相同的实例。 但是,如果你复制一个可变列表,那么 copy
确定就是一个新实例,因此在改变原始 list 时,要保证没有副作用产生:
var list = new ArrayList<String>(); var copy = List.copyOf(list); System.out.println(list == copy); // false
当创建不可变的 maps 时,你不需要亲自去创建一个完整的 map 集合,你可以直接通过 Map.of
来进行创建:
var map = Map.of("A", 1, "B", 2); System.out.println(map); // {B=2, A=1}
Java 11中的不可变集合仍然使用旧Collection API中的相同接口。 但是,如果尝试通过添加或删除元素来修改不可变集合,则会抛出java.lang.UnsupportedOperationException。 幸运的是,如果你尝试改变不可变集合,Intellij IDEA会通过检查发出警告。
Streams
Java8中介绍的Stream也新增了方法。Stream.ofNullable
可以从单个元素中构造一个Stream:
Stream.ofNullable(null).count(); // 0
dropWhile
与 takeWhile
方法可以用来决定stream中的哪些元素可以被抛弃:
Stream.of(1, 2, 3, 2, 1) .dropWhile(n -> n < 3) .collect(Collectors.toList()); // [3, 2, 1] Stream.of(1, 2, 3, 2, 1) .takeWhile(n -> n < 3) .collect(Collectors.toList()); // [1, 2]
如果你对Streams不是很熟悉,你可以看看这篇文章 Java 8 Streams Tutorial.
Optionals
Optionals
也新增了一些非常好用的方法。例如,现在你可以简单地将 Optionals
转换为 Streams
,或者使用另一个optional作为一个空optional的fallback。
Optional.of("foo").orElseThrow(); // foo Optional.of("foo").stream().count(); // 1 Optional.ofNullable(null) .or(() -> Optional.of("fallback")) .get(); // fallback
Strings
最基本的类之一 String
新增了一些辅助方法,用以修剪或检查空格以及对字符串进行流化处理:
" ".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,这是一个在处理原始数据流时非常常见的用例。
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 8迁移到11时最有趣的语言新API功能。但是功能列表并没有在这里结束。 最新的Java版本中包含了更多内容:
- Flow API for reactive programming
- Java Module System
- Application Class Data Sharing
- Dynamic Class-File Constants
- Java REPL (JShell)
- Flight Recorder
- Unicode 10
- G1: Full Parallel Garbage Collector
- ZGC: Scalable Low-Latency Garbage Collector
- Epsilon: No-Op Garbage Collector
- Deprecate the Nashorn JavaScript Engine
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
JavaScript创建对象(三)——原型模式
在JavaScript创建对象(二)——构造函数模式中提到,构造函数模式存在相同功能的函数定义多次的问题。本篇文章就来讨论一下该问题的解决方案——原型模式。 首先我们来看下什么是原型。我们在创建一个函数时,这个函数会包含一个属性prototype,这个属性是一个指针,它指向一个对象——该函数的原型对象,这就是原型,它包含了该函数类型的所有实例可共享的属性和方法,见下面示意图: 如图所示,声明了一个函数Person。在JavaScript中,一个函数被声明的同时就具有了一些属性,其中有一个叫做prototype,它指向了该函数的原型对象,即上述示例中的Person Prototype。同时,这个原型对象有一个叫做constructor的属性反过来又指向了该函数对象。 当我们创建一个函数的实例时,例如上面的var personObj = new Person('张三', 12);,这个实例也会有一个属性指向该函数的原型对象,在Chrome的开发工具中显示为__proto__。 上面我们说原型的属性可以被该函数类型的所有实例所共享,那具体是怎么实现呢?看下面的示例: function Per...
- 下一篇
Go 逃逸分析
1 前言 所谓逃逸分析(Escape analysis)是指由编译器决定内存分配的位置,不需要程序员指定。 函数中申请一个新的对象 如果分配 在栈中,则函数执行结束可自动将内存回收; 如果分配在堆中,则函数执行结束可交给GC(垃圾回收)处理; 有了逃逸分析,返回函数局部变量将变得可能,除此之外,逃逸分析还跟闭包息息相关,了解哪些场景下对象会逃逸至关重要。 2 逃逸策略 每当函数中申请新的对象,编译器会跟据该对象是否被函数外部引用来决定是否逃逸: 如果函数外部没有引用,则优先放到栈中; 如果函数外部存在引用,则必定放到堆中; 注意,对于函数外部没有引用的对象,也有可能放到堆中,比如内存过大超过栈的存储能力。 3 逃逸场景 3.1 指针逃逸 我们知道Go可以返回局部变量指针,这其实是一个典型的变量逃逸案例,示例代码如下: package main type Student struct { Name string Age int } func StudentRegister(name string, age int) *Student { s := new(Student) //局部变量s...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Docker安装Oracle12C,快速搭建Oracle学习环境
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Red5直播服务器,属于Java语言的直播服务器