共计 7795 个字符,预计需要花费 20 分钟才能阅读完成。
本篇将介绍Java 8及以上高版本(11、17、21)的新特性。Java每年都会发布两个版本,但真正会被大规模使用的是三年一个的长期支持(LTS)版本,也就是长期维护版本。这意味着Java 8、Java 11、Java 17和Java 21才可能被大规模使用。本篇文章写于24年3月,最近的LTS版本为Java 21,下一个LTS版本为Java 25。
Java 8 新特性
Java 8于2014年3月18日发布。其主要增加了以下特性:
- Lambda表达式:允许把函数作为一个方法的参数,即函数作为参数传递到方法中。
// 使用Lambda表达式
Runnable runnable = () -> System.out.println("Hello, World!");
runnable.run();
- 默认方法与静态方法:允许使用关键字
default
和static
给一个接口提供一个默认方法和静态方法。
interface MyInterface {
// 默认方法
default void sayHello() {
System.out.println("Hello, World!");
}
// 静态方法
static void greet() {
System.out.println("Welcome!");
}
}
class MyClass implements MyInterface {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.sayHello();
MyInterface.greet();
}
}
- 方法引用:方法引用可以直接引用已有Java类或对象的方法或构造器。一般与Lambda联合使用,方法引用可以使语言的结构更紧凑简洁,减少冗余代码。
// 方法引用
User user = new User();
boolean isLegalName = list.stream().anyMatch(user::isLegalName);
- Stream API:Stream API提供了一种更简洁、更易读的方式来处理集合数据。这是对Java集合运算和表达的高阶抽象。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// 使用Stream API过滤出长度大于 4 的名字并打印
names.stream()
.filter(name -> name.length() > 4)
.forEach(System.out::println);
- Optional类:Optional类已经成为Java 8类库的一部分,它可以表示一个值存在或者不存在,避免了直接使用null的情况,主要用来解决空指针异常。
Optional<String> optional = Optional.of("Hello");
Optional<String> emptyOptional = Optional.empty();
Optional<String> optional = Optional.ofNullable(null);
if (optional.isPresent()) {
System.out.println("值存在:" + optional.get());
} else {
System.out.println("值不存在");
}
Java 11 新特性
Java 11于2018年9月发布,是继Java 8之后的第一个LTS版本。Oracle也在2019年1月停止支持Java 8。因此,很多Java 8的项目在这之后都会逐渐升级。
OpenJDK官方文档:https://openjdk.org/projects/jdk/11/
- 新的String方法:Java 11向String类添加了一些新方法:
isBlank
、line
、strip
、stripLeading
、stripTrailing
和repeat
。这些方法主要是从多行字符串中提取非空白字符串和去除换行符或空格符的。
public class Solution {
public static void main(String[] args) {
String repeated = "Hello, ".repeat(3);
System.out.println(repeated); // "Hello, Hello, Hello, "
String text = " Trim me ";
System.out.println(text.strip()); // "Trim me"
System.out.println(text.stripLeading()); // "Trim me "
System.out.println(text.stripTrailing());// " Trim me"
}
}
- 新的文件操作方法:Java 11向Files类添加了一些静态方法
readString
和writeString
。让文件的读写更加容易。
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
public class Solution {
public static void main(String[] args) throws IOException {
String path = System.getProperty("user.dir");
File tempFile = new File(path + File.separator + "temp.txt");
Files.writeString(tempFile.toPath(),"Hello! Java 11");
String readStr = Files.readString(tempFile.toPath());
System.out.println(readStr); // "Hello! Java 11"
}
}
- 局部变量自动推断:Java 11引入了var关键字,可以在局部变量声明时使用,编译器会根据初始化表达式的类型推断变量的类型。
import java.io.IOException;
import java.util.List;
public class Solution {
public static void main(String[] args) throws IOException {
var name = "Alice";
var age = 30;
var list = List.of("Apple", "Banana", "Orange");
System.out.println(name + " " + age + " " + list.toString());
}
}
- HTTP客户端API:Java 11引入了标准的HTTP客户端API,可以用于发送HTTP请求和处理响应。
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Solution {
public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://github.com"))
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}
}
- 通过
java
命令直接允许*.java
源码文件:在Java 8中,我们要运行Java源文件,需要先通过javac
命令将源码文件编译为字节码文件,再使用java
命令将字节码文件交由虚拟机解释运行。但在Java 11中,我们可以直接通过java HelloWorld.java
指令直接运行Java。
java Solution.java
Java 17新特性
Java 17于2021年9月正式发布,是Java 11之后的又一个LTS版本。Java 17最多可以支持到2029年9月,同时,在Spring 6.x和Spring Boot 3.x中,已经强制要求最低版本为Java 17。
Java 17是继Java 8之后非常重要的LTS版本,是Java社区8年以来的重要成果。
OpenJDK官方文档:https://openjdk.org/projects/jdk/17/
-
恢复严格的浮点语义:在以前的Java版本中,为了提高性能,有时候会对浮点运算进行优化,这就导致结果可能不符合严格的浮点运算规则。在Java 17中,恢复了浮点运算的严格要求,确保浮点运算的结果更符合预期,避免了一些潜在的精度问题。这一特性是底层的改变,而不是代码层面的改变,因此,若想让浮点运算更为严格,只需将JDK升级为17即可。
-
提供更强大的伪随机数生成器:Java 17引入了一些增强的伪随机数生成器(PRNG)算法,以提高随机数生成的质量和性能。
import java.util.random.RandomGenerator;
public class Solution {
public static void main(String[] args) {
String[] algorithms = {
"L32X64MixRandom",
// "L32X64StarStarRandom", //
"L64X128MixRandom",
"L64X128StarStarRandom",
"L64X256MixRandom",
"L64X1024MixRandom",
"L128X128MixRandom",
"L128X256MixRandom",
"L128X1024MixRandom",
"Xoshiro256PlusPlus",
"Xoroshiro128PlusPlus"
};
for(String algorithm: algorithms){
try {
RandomGenerator randomGenerator = RandomGenerator.of(algorithm);
int randomNum = randomGenerator.nextInt();
System.out.println(algorithm+"=>" + randomNum);
}catch (IllegalArgumentException e){
System.out.println(algorithm);
}
}
}
}
-
删除Applet API:Java Applet是一种能运行在网页上的小程序,在JDK 9的时候,Applet API就被标记为过时,直到JDK 17才将Applet API进行了删除。
-
增强switch的自动匹配能力(预览):在之前的Java版本中,switch语句只能基于常量值进行匹配,而引入了模式匹配后,switch语句可以使用更灵活的模式来匹配表达式的值。
public class Solution {
public static void main(String[] args) {
Object obj = "Hello";
switch (obj) {
case String s -> System.out.println("String length: " + s.length());
case Integer i -> System.out.println("Integer value: " + i);
default -> System.out.println("Unknown type");
}
}
}
注意:switch的模式匹配在JDK 17中只处在预览中,要想使用模式匹配,需要在编译时添加
--enable-preview
参数开启。例如:
javac --enable-preview --release 17 Solution.java
java --enable-preview Solution
- 封闭类(Sealed Class):封闭类允许开发者限制一个类的子类的继承关系,从而控制类的层次结构,提高代码的安全性和可维护性。在Java 17中,我们可以使用
sealed
关键字来声明一个封闭类,同时使用permits
关键字列出该类允许继承的子类。封闭类的子类可以继续使用sealed
来限制子类,或者使用no-sealed
关键字来表示子类不受限制。
// 定义一个受限制的Shape封闭类,该类只允许被Circle和Rectangle继承。
sealed class Shape permits Circle, Rectangle {
}
// 封闭类的子类只允许用final、sealed、non-sealed修饰
final class Circle extends Shape {
}
non-sealed class Rectangle extends Shape {
}
class OtherShape extends Rectangle {
}
- 废弃安全管理器API:安全管理器是Java平台中用于控制应用程序对系统资源的访问权限的重要组件。然而随着Java平台的发展和安全模型的变化,安全管理器的使用逐渐减少。JDK 17或许向开发者发出了信号,鼓励开发者不再依赖于安全管理器,而是寻找其它替代方案来管理程序的安全性。
Java 21 新特性
JDK 21于2023年9月19日正式发布,是JDK 17之后的又一个LST版本。在JDK 21发布以后,最吸引开发者的一点就是引入了“虚拟线程”。
虚拟线程是轻量级线程,可以显著减少编写、维护和观察高吞吐量并发应用程序的工作量。虚拟线程并不会改变传统Java的并发模型,也不是并行处理大型数据集的首选方式。
- 虚拟线程:虚拟线程在JDK 19中就被提出, 并作为预览部分,直到JDK 21才正式作为Java新特性。线程和进程一样,都是程序中非常昂贵的资源,因此我们的Java程序一般都会限制线程大小。比如常用的Tomcat,每一个请求都对应一个线程处理,同时限制处理请求的线程数量防止线程过多而导致服务器崩溃。这在一定程度上限制了Web服务的吞吐量。对于这种需要提高吞吐量的场景,使用虚拟线程将会大大改善这种情况。对开发者来说,虚拟线程在使用体验上和
Thread
几乎没有区别,相比之下,虚拟线程资源占用得非常少,同时,虚拟线程是一种即用即启动的资源,不应该被池化存储。下面将通过一个小实验来对比虚拟线程和传统线程的处理能力。
创建10000个线程,每个线程被创建出来后休眠1s再结束,计算整个过程用时。
import java.time.Duration;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class Solution {
public static void main(String[] args) {
long start = System.currentTimeMillis();
try (var executor = Executors.newFixedThreadPool(10000)){
IntStream.range(0,10_000).forEach(i -> {
executor.submit(()->{
Thread.sleep(Duration.ofSeconds(1));
return i;
});
});
}
long end = System.currentTimeMillis();
System.out.println("use time:" + (end - start) + "ms");
}
}
传统操作会在突破线程限制时报错:
java.lang.OutOfMemoryError
import java.time.Duration;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class Solution {
public static void main(String[] args) {
long start = System.currentTimeMillis();
try(var executor = Executors.newVirtualThreadPerTaskExecutor()){
IntStream.range(0,10_000).forEach(i -> {
executor.submit(()->{
Thread.sleep(Duration.ofSeconds(1));
return i;
});
});
}
long end = System.currentTimeMillis();
System.out.println("use time:" + (end - start) + "ms");
}
}
用时:1135ms
- 统一有序集合的操作接口:Java中的集合类(List、Deque、Linked种类的set和map)一般使用频率非常高,但是在JDK21之前,这些集合类的使用操作并不统一,容易造成混乱。
在JDK 21中,增加了SequencedCollection
、SequencedSet
和SequencedMap
接口,并且在SequencedColletion
接口中定义了有序集合中常用的操作方法。
addFirst
addLast
getFirst
getLast
removeFirst
removeLast
reversed
在SequencedMap
接口中又增加了Map的操作方法:
firstEntry
lastEntry
pollFirstEntry
pollLastEntry
putFirst
putLast
reversed
sequencedEntrySet
sequencedKeySet
sequencedValues
- switch模式匹配:在JDK 17中,switch的模式匹配被划为预览中,需要通过
--enable-priview
参数才能开启。但在JDK 21中,已经正式支持了。 - record模式:record模式用于定义数据对象的结构,该数据对象不允许继续修改。record模式是Java面向数据编程的基础,并将在以后逐渐发展,我会在之后单独写一篇关于record的介绍及使用文章。下面只进行简单的演示:
public class Solution {
public static void main(String[] args) {
Person person = new Person("Bob",21);
if(person instanceof Person(String name,Integer age)){
System.out.println(name + " " + age);
}
}
}
record Person(String name,Integer age){};