Java升级版本中,什么是我们小开发真正可用的

java8被使用多年,但现在新的javaLTS版本不断更新,很多企业都开始了java的更新换代,从java11-java17-java21, 但是说实在的,到底有多少功能是我们小开发真正在意的呢,有多少更新是我们日常开发中真正可以被应用的呢,本文进行了一些总结,希望可以帮助大家快速升级java版本和掌握

Java8(LTS)

Lambda表达式(core)

在lambda之前我们使用的古老方式是 匿名内部类

List<String> list = Arrays.asList("A", "B", "C");
Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return o1.compareTo(o2);
    }
});

lambda表达式完全简化了这一写法

List<String> list = Arrays.asList("A", "B", "C");
list.sort((o1, o2) -> o1.compareTo(o2));

lambda的写法实际并不是最重要的,重要的其实是背后的 流式操作(stream)

函数式接口 + 自定义函数传参

java8引入了大量的函数接口来提升代码的抽象能力,这里指的是用于构建通用工具,回调逻辑之类,同时将函数能够以参数的方式传递给另一个函数

//old way
public class Printer {
    public void print(String msg) {
        System.out.println(msg);
    }
}

Printer printer = new Printer();
printer.print("Hello");
//new way(combine lambda)
Consumer<String> printer = msg -> System.out.println(msg);
printer.accept("Hello");

Stream API

Stream API又称为声明式数据处理,

stream对于处理list在日常的使用中,让我们减少了非常多的重复for循环

stream 的设计初衷就是为了支持声明式、链式、惰性求值的数据处理方式

//before java8
List<String> list = Arrays.asList("a", "bb", "ccc");
List<String> filtered = new ArrayList<>();

for (String s : list) {
    if (s.length() > 1) {
        filtered.add(s.toUpperCase());
    }
}
//java8 链式调用,可读性更高,便于组合对于list的复杂操作
List<String> result = list.stream()
    .filter(s -> s.length() > 1)
    .map(String::toUpperCase)
    .collect(Collectors.toList());

Optional类

optional类的存在是为了防止代码中频繁的NullPointerException,也就是说在使用Optional类后,即使是null值也可以正常运行程序,不抛错出来

//before java8,  所有null值都要记得手动判断
public String getName(User user) {
    if (user != null && user.getName() != null) {
        return user.getName();
    }
    return "unknown";
}
//java8
public String getName(User user) {
    if (user != null && user.getName() != null) {
        return user.getName();
    }
    return "unknown";
}

default method

接口默认方法, Java8为了支持函数式编程和流式处理,对核心接口如 Iterable, Collection, List, Map 等增加了很多新的方法
但是如果没有默认方法机制,这些接口的新增方法将导致旧代码全部崩溃,JDK 自己都编译不过去。

所以,为了在不破坏已有代码的前提下演进接口设计,Java 8 引入了 default

//before java8, 接口与无法有实现,必须新建抽象类来提供默认实现

//java8
interface Hello {
    default void sayHi() {
        System.out.println("Hi");
    }
}

新的时间/日期 API(java.time)

java8以前我们用calendar和date进行时间操作,但是这两个类并非是线程安全的,所以java8引入了LocalDate

//before java8
Calendar cal = Calendar.getInstance();
cal.set(2023, Calendar.JULY, 1);
Date date = cal.getTime();

//java8
LocalDate date = LocalDate.of(2023, 7, 1);
  1. 不可变对象 天然线程安全,多个线程访问同一个 LocalDate 实例不会发生并发冲突。
  2. 所有对 LocalDate 的操作(比如 plusDays()minusWeeks()都会返回一个新对象,而不修改原对象。这样就不会担心数据被意外改动
  3. 还是函数式编程,LocalDate 的不可变性让它天然适合在函数式链式操作中使用,说人话就是在整个数据操作过程中,不会破坏任何数据

不会。LocalDate轻量级的 final 类,且广泛使用了 对象复用、缓存逃逸分析优化(JVM 可自动将短生命周期对象分配在栈上),性能非常优秀,远比 Date/Calendar 高效、安全。

方法引用(::)

::并不只是单纯的两个冒号,它有一个新的名称,叫做方法引用符,实际上是lambda表达式的语法糖,存在的主要意义是极简语法,提高可读性,与lambda打配合,使代码更优雅

list.forEach(System.out::println);  // 等价于 s -> System.out.println(s)

重复注解(@Repeatable)

实际上在日常开发中不是很常用,但是嗯~,还是有必要知道的

//以前一个注解只能使用一次,现在可以多次重复使用,便于元数据编程(又是很大的一部分了,不在此赘述)

//java8 now
@Hint("hint1")
@Hint("hint2")
class Person {}

异步编程增强CompletableFuture

CompletableFuture 是对 Future 的增强,提供了更强的异步能力、链式调用、组合任务等现代异步编程所需的一切能力

//传统Future
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> {
    Thread.sleep(1000);
    return "Hello";
});
String result = future.get(); // 阻塞
System.out.println(result);

//存在的问题
❌ 只能阻塞等待结果	必须调用 get() 才能拿到结果,而且是阻塞的
❌ 无法主动完成	只能由线程池完成任务,不能外部 setResult
❌ 不支持任务组合	想要两个 Future 组合结果非常麻烦
❌ 不支持异步回调	没有 onComplete、thenApply 等回调
❌ 不支持异常处理链	错误处理不方便,必须 try-catch
❌ 不支持流式编程	不能链式调用、组合任务等
//非阻塞,异步回调
CompletableFuture.supplyAsync(() -> "Hello")
    .thenApply(s -> s + " World")
    .thenAccept(System.out::println); // 输出 Hello World

//多任务组合
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "A");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "B");

CompletableFuture<String> combined = future1.thenCombine(future2, (a, b) -> a + b);
System.out.println(combined.get()); // 输出 AB

//优雅支持异常处理链
CompletableFuture.supplyAsync(() -> {
    if (true) throw new RuntimeException("boom");
    return "ok";
}).exceptionally(ex -> "error: " + ex.getMessage())
  .thenAccept(System.out::println); // 输出 error: boom

//支持手动完成,超时控制等高级功能
CompletableFuture<String> future = new CompletableFuture<>();
	// 可用于回调模式中手动完成
future.complete("manually set");

Java9

JPMS

关于这个,暂时仅谈论概念层面,不在此过多阐述

JPMS是将JDK变成模块化,支持更细粒度的依赖管理和封装,非常适合大型项目

不可变集合的快速创建

这个东西非常实用,尤其是在进行测试的时候,我们不用再从new开始为list数据赋值
而且简洁,线程安全,避免了Arrays.asList返回固定大小可变集合的坑

List<String> list = List.of("a", "b", "c");
Set<Integer> set = Set.of(1, 2, 3);
Map<String, Integer> map = Map.of("x", 10, "y", 20);

//注意,数据大小超过10个以上会有问题

Stream Api增强

新增了一些方法,例如takeWhile, dropWhile,包括iterate新增了带条件的重载,更为灵活

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

List<Integer> taken = numbers.stream()
    .takeWhile(n -> n < 4)  // 从头开始取,直到条件不满足
    .collect(Collectors.toList()); // 结果 [1, 2, 3]

List<Integer> dropped = numbers.stream()
    .dropWhile(n -> n < 4)  // 从头开始跳过,直到条件不满足
    .collect(Collectors.toList()); // 结果 [4, 5, 6]

Optional增强

增加了ifPresentOrElse, 避免了手动写if(opt.isPresent)

Optional<String> opt = Optional.of("Hello");

opt.ifPresentOrElse(
    s -> System.out.println("Value: " + s),
    () -> System.out.println("No value")
);

CompletableFuture 增强

增加异步处理超时

CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> "task")
    .completeOnTimeout("timeout", 1, TimeUnit.SECONDS);

cf.thenAccept(System.out::println);

Java10

新特性速览

特性类别具体内容是否日常可用用途说明
✅ 局部变量类型推断var 关键字:局部变量自动推断类型✅ 强烈推荐简化代码,提升可读性与开发效率
JVM 性能优化GC 改进(如 G1 默认使用并行 full GC)❌ 底层提升大堆场景的 GC 性能
容器环境支持JVM 能正确识别容器的 CPU 和内存限制(如 Docker 限制)✅ DevOps 场景更准确的资源控制,适合容器部署
应用类数据共享(AppCDS)AppCDS(Application Class-Data Sharing)支持所有类✅ 部署优化减少冷启动时间,尤其适合大型项目
GC 接口标准化将 GC 接口标准化,方便引入新的垃圾回收器❌ 底层为后续 GC 创新奠定基础
root-certificate 集成开箱即用支持默认 CA 根证书列表✅ 安全相关不再手动配置 SSL 证书(适用于安全连接)

Var 局部类型推断

java是强类型语言,var的存在并不是将java从强类型转换为若类型语言的,主要使用场景如下:

  1. 泛型声明,避免重复书写类型
  2. 遍历中间变量
  3. 临时变量,例如stream的中间变量,作为结果的缓存(非常常用)
//java会根据右侧推测类型
var list = new ArrayList<String>();
var map = new HashMap<String, Integer>();
var name = "Dean";
var count = 42;

//作为临时变量来处理stream
public class VarInStreamExample {
    public static void main(String[] args) {
        List<String> names = List.of("Alice", "Bob", "Charlie", "David");

        List<String> result = names.stream()
            .map(name -> {
                var upper = name.toUpperCase();      // var 用于临时变量
                var formatted = "[" + upper + "]";   // 再处理
                return formatted;
            })
            .collect(Collectors.toList());

        result.forEach(System.out::println);
    }
}

//当然也可以在filter中用
List<String> filtered = names.stream()
    .filter(name -> {
        var length = name.length();   // 使用 var 作为中间值
        return length > 3;
    })
    .collect(Collectors.toList());

filtered.forEach(System.out::println);

//但是不能在lambda的参数中使用(JDK 10 不支持,但 JDK 11+ 支持 var name ->)
names.stream().map((var name) -> name.toUpperCase()); // ❌

最重要的是,var因为是从右侧推测类型的,所以必须立即初始化,不要写未初始化的var 变量

Java 11 (LTS)

新特性速览

特性类别特性简介是否日常可用实用价值
✅ 语法增强var 可用于 Lambda 参数✅是简化Lambda参数声明,支持加注解
✅ 标准 API 增强字符串、文件、集合新方法✅ 是更加高效优雅处理字符串,文件,集合
✅ HTTP Client新的 HttpClient 替代 HttpURLConnection,支持异步和 HTTP/2✅ 是发起 HTTP 请求更方便、功能更全
✅ 单文件运行java Hello.java 直接运行✅ 小工具开发、脚本场景编写/调试临时脚本,快速验证 Java 代码段
✅ 本地接口调用(Nestmates)支持类之间访问私有成员⭕ JVM 底层优化JVM 优化嵌套类访问逻辑,语法级变化小
✅ GC 改进支持 ZGC、Epsilon GC⭕ 特殊性能调优时用提高吞吐量或最小暂停时可选,部署调优时用
❌ 模块移除移除了 Java EE 和 CORBA 模块⚠️ 如果用到旧 API 需额外依赖使用 Java EE API 时需引入第三方依赖
✅ Flight Recorder & Mission ControlJava Flight Recorder(JFR)与 Mission Control 开源并集成⭕ 性能调优分析工具性能调试与运行分析更强大,替代 commercial 工具
✅ 异常处理改进新增 java.lang.invoke.ConstantDescvar 在 try-with-resources 中支持❌ 很少用多用于库/框架开发或编译器支持

String增强

Java11对于字符串的处理上,提供了更多的API

var text = "  \n  hello  \n";

// 去除空白行
System.out.println(text.isBlank());        // true(全是空白)
// 去除首尾空白字符
System.out.println(text.strip());          // "hello"
// 去除前导空白
System.out.println(text.stripLeading());   // "hello  \n"
// 去除尾部空白
System.out.println(text.stripTrailing());  // "  \n  hello"
// 行切割
"abc\ndef\nghi".lines().forEach(System.out::println); // 输出3行
// 重复字符串
System.out.println("ha".repeat(3));        // "hahaha"

文件操作增强

Files.readString() / writeString() 简化了文件读写,不仅语义更加简化直观,而且减少了中间类型,编码处理,降低的文件处理的错误率

//java8  read
Path path = Paths.get("hello.txt");
String content = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);

//java11 read
Path path = Paths.get("hello.txt");
String content = Files.readString(path);

//java8 write
Path path = Paths.get("output.txt");
Files.write(path, "Hello".getBytes(StandardCharsets.UTF_8));

//java11 write
Path path = Paths.get("output.txt");
Files.writeString(path, "Hello"); 

HttpClient

HttpCient作为HttpURLConnection的替代品出现,不仅支持同步异步,还内建支持HTTP/1.1 和HTTP2, 从此不再需要第三方库也能优雅发送请求,同时还支持连接池,超时控制等

//java8 send a request
URL url = new URL("https://httpbin.org/get");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
    System.out.println(line);
}
in.close();
//java 11
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://httpbin.org/get"))
        .build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());

Var增强

java10引入了var,java11对于var进行了升级,使之支持Lambda参数,可用配合注解使用

//java8 lambda表达式的参数声明
list.forEach((String s) -> System.out.println(s));

//java11
list.forEach((@Nonnull var s) -> System.out.println(s)); //支持注解

单文件java源码运行

Java 11 引入了 单文件 Java 源码直接运行功能,可以像脚本语言一样使用
这在使用时感受并不是很强烈,但是这一个特性的存在让Java变得更为轻量,快速,上手门槛进一步降低,可以像python一样,直接写一些小工具,学习脚本,临时测试等东西,实际是非常大的提升

在过去,我们如果想要运行一个java文件,需要先编译后运行

javac Hello.java
java Hello

//但对比同期的其他语言,真是输在起跑线上
python hello.py
node hello.js
go run hello.go

//因此java11为了降低这个起步成本,缩小与其他语言的差异,引入了单文件运行
java Hello.java  // 省略编译

实际上在Java9的时候,就有这个势头了,java9引入了 jshell,但 JShell 更适合交互式试验,不适合写一段完整脚本。而单文件运行是需要同时满足以下条件的:

  • 写一个文件就能运行 ✅
  • 支持 main 方法和完整类结构 ✅
  • 适合自动化小脚本、CI 工具等场景 ✅

模块移除

java11 移除了Java EE相关的一些重要模块,但这实际上也增加了大家从java8升级到java11的成本,如果你在java8使用这些模块,那么升级后java11中需要自行添加三方依赖,不仅有可能存在着版本兼容的问题,还可能提高安全风险

移除模块包含内容
java.xml.bindJAXB
java.activationJavaBeans Activation
java.xml.wsJAX-WS(SOAP)
java.corbaCORBA

Java 12 & Java 13

Java12和Java13 都不是LTS的版本,相对于Java11来说,更新也不是很多,且多为语言层面的小幅增强或JVM内部改进,而且是临时试用特性,并不推荐正式部署,所以不用花太多时间在上面

增强版Switch

实际是预览特性,java14才成为标准,所以在java12需要通过命令行开启

java --enable-preview --source 12 MyClass.java

//java8 写法
String dayType;
switch (day) {
    case MONDAY:
    case TUESDAY:
    case WEDNESDAY:
    case THURSDAY:
    case FRIDAY:
        dayType = "Working Day";
        break;
    case SATURDAY:
    case SUNDAY:
        dayType = "Weekend";
        break;
    default:
        throw new IllegalArgumentException("Invalid day");
}

//java12  简洁了巨多
String dayType = switch (day) {
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Working Day";
    case SATURDAY, SUNDAY -> "Weekend";
    default -> throw new IllegalArgumentException("Invalid day");
};

解释一下为什么不再需要break了,在 Java 12 中,switch 被设计为一种 表达式(而不仅仅是语句),每个分支使用 ->只能返回一个值,不允许 fall-through(穿透),因此 break 就不再需要了,也没有意义了。另外如果遇到复杂情况,例如每个分支需要多语句是,
可以使用 yield

Collectors.teeing()

可以合并两个流的结果,适合场景就是 可以同时计算 平均值和总和,最大值和最小值等双结果

//java8 写两次
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
int sum = numbers.stream().mapToInt(i -> i).sum();
double avg = numbers.stream().mapToInt(i -> i).average().orElse(0);

//java12 
var result = numbers.stream().collect(
    Collectors.teeing(
        Collectors.summingInt(i -> i),
        Collectors.averagingInt(i -> i),
        (sum, avg) -> Map.of("sum", sum, "avg", avg)
    )
);
System.out.println(result); // {sum=15, avg=3.0}

CompactNumberFormat紧凑数字格式

为UI而生,生成可读性更强的数据

NumberFormat shortFormat = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
System.out.println(shortFormat.format(1_000_000)); // 1M

Text Blocks 文本块

这是Java13中的预览特性,目的是为了解决Java多行字符串写起来太烦的问题,因为是预览特性,所以也要加编译参数

javac --enable-preview --release 13 Hello.java
java --enable-preview Hello

Sql

//java13之前
String sql = "SELECT id, name, age FROM users\n" +
             "WHERE age > 18\n" +
             "ORDER BY name;";

//java13, 对于sql无需再使用额外的换行符和转义符,缩进也更友好
String sql = """
    SELECT id, name, age FROM users
    WHERE age > 18
    ORDER BY name;
    """;

Json

//java13之前
String json = "{\n" +
              "  \"name\": \"Alice\",\n" +
              "  \"age\": 30\n" +
              "}";

//java13
String json = """
    {
      "name": "Alice",
      "age": 30
    }
    """;

除此之外,对于写HTML模板,构造内联Shell脚本,正则模板,配置模板都更简单,以下是Text Bolck的主要特点和语法说明

特性说明
使用 """ 包裹开始于行尾的 """,结束于独立的一行 """
自动换行每行都自动加 \n(无需写 \n
自动缩进对齐会根据最小缩进行统一“左移”
可以使用 \n \t支持传统转义字符
可写 \"不必像以前一样转义双引号

Java14

Java14是2020年发布,依然是一个短期脚本,但是它带来了一些非常关键的语言特性预览,例如java12中提到的switch表达式从预览转为正式语法,添加了record数据类,做了NPE的优化等

新特性速览

特性类别具体内容是否日常可用用途说明
✅ 语法增强switch 表达式(标准化)✅ 是从 Java 12 预览转为正式语法,写法现代且更安全简洁
✅ 新语法预览record 数据类(预览)✅ 是(开启预览)简洁定义只用于保存数据的类
✅ NPE 优化更清晰的 NullPointerException 信息✅ 是报错信息直接告诉你是哪个变量是 null
✅ 工具增强jpackage(打包工具)✅ 是可直接打包为原生可执行文件(Windows、Mac、Linux)
⭕ 语法预览instanceof 模式匹配(预览)⭕ 可开启类型判断 + 自动类型转换,减少样板代码
⭕ GC 改进ZGC、G1 等性能优化❌ 少用大型系统调优用
⭕ JFR 持续事件Flight Recorder 支持长期运行的数据采集❌ 少用性能分析用

Record

switch在java12已经对比过,因此不再赘述,Record类诞生是为了承载数据的类(DTO),自动生成 构造器 + getter + equals + hashCode + toString
Record的存在使之更适合作为 POJO、DTO、VO、响应体,怎么说呢,为Web而生吧

//java14之前
public class User {
    private final String name;
    private final int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String name() { return name; }
    public int age() { return age; }

    @Override public String toString() { return "User{name=" + name + ", age=" + age + "}"; }

    // 还要写 equals, hashCode...
}

//java14
public record User(String name, int age) {}

NPE更清晰

自Java14开始,当你遇到空指针时,报错会告诉你哪一个变量是null,而不再是模糊的NullPointerException,对于debug非常友好

//java14之前
Exception in thread "main" java.lang.NullPointerException
    at MyApp.main(MyApp.java:6)

//java14
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Person.getName()" because "person" is null

instanceof 模式匹配(预览)

Java 14 开始支持以下语法(Java 16 成为正式语法),模式匹配会让代码更安全(编译器保证类型)

//java14之前,需要手动强制转换
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

//java14,自动转
if (obj instanceof String s) {
    System.out.println(s.length());
}

Java15

Java15主要也是针对语言特性方面的更新,其次就是对于JVM的垃圾收集器进行了一些更新,除此之外弃用并移除了部分安全算法

Text Blocks(多行字符串)

在Java13的时候进行了测试版本的使用,在Java15的时候正式标准化

//java14及以前(大量的+和\n)
String json = "{\n" +
              "    \"name\": \"Alice\",\n" +
              "    \"age\": 25\n" +
              "}";
System.out.println(json); 

//java15
String json = """
    {
        "name": "Alice",
        "age": 25
    }
    """;
System.out.println(json);

CharSequence.isEmpty()

使之与 String.isEmpty() 保持一致

//java15前
CharSequence cs = "hello";
if (cs.length() == 0) {
    System.out.println("Empty");
}

//java15
CharSequence cs = "hello";
if (cs.isEmpty()) {
    System.out.println("Empty");
}

Scaled Classes(封闭类)

限制了哪些类可以继承某个类,以手动管理类权限,提升安全性和可维护性

//java15以前,无法直接限制
abstract class Shape {}
class Circle extends Shape {}
class Square extends Shape {}
// 其他包也能随便继承 Shape

//java15
public sealed class Shape permits Circle, Square {}
final class Circle extends Shape {}
final class Square extends Shape {}
// 其他类无法继承 Shape

Java16

Java16是2021年发布的短期版本(Non-LTS),生命周期仅有6个月,主要延续了java14和java15的语言改进方向

instanceof 模式匹配(Pattern Matching)

该模式从java14开始预览,在java16中正式发布,从此避免了在instanceof 后还要进行强制类型转换

//java15之前
Object obj = "Hello Java";
if (obj instanceof String) {
    String s = (String) obj; // 必须手动强制类型转换
    System.out.println(s.toUpperCase());
}

//java16
Object obj = "Hello Java";
if (obj instanceof String s) { // 直接定义变量 s
    System.out.println(s.toUpperCase());
}

Record类型正式版

也是一样java14预览,java16正式发布

//java15及以前
public final class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String name() { return name; }
    public int age() { return age; }
    @Override
    public String toString() { return name + " (" + age + ")"; }
}

//java16
public record Person(String name, int age) { }
  • 自动生成构造器、toString()equals()hashCode()
  • 不可变性更安全
  • 写法极度简洁

Stream.toList()

//java15及以前
List<String> list = Stream.of("A", "B", "C")
        .collect(Collectors.toList());
        
//java16
List<String> list = Stream.of("A", "B", "C").toList();

Java17(LTS)

Java17是继Java11之后的一个TLS版本,Java15和java16中以预览出现的一些功能,在Java17中正式发布,例如我们提到的Sealed Class,switch的新写法,Stream.toList()等,同时废除了一些几乎不再被使用的特性(Applets,RMI Activation 系统(过时的分布式机制),Security Manager(计划未来移除))

因为之前以及提到过Sealed Class, switch的pattern格式,Stream.toList的更新用法,所以在此不再重复赘述

增强随机数API

//java16及以前
Random random = new Random();
int num = random.nextInt(10);

//java17
RandomGenerator generator = RandomGenerator.of("L64X128MixRandom");
int num = generator.nextInt(10);

优点是

  • 可选择不同算法(如更均匀分布、更高性能的生成器)
  • 支持流式生成随机数

Java18

Java18 是2022年发布的,生命周期仅6个月的短期支持版本, 核心在java17的基础上做了小幅的语言改进,JDK的内部优化,以及一些实验性的API

SimpleWebServer

//java17及以前,启动一个HTTP服务器需要写很多代码或引入第三方包
import com.sun.net.httpserver.HttpServer;
import java.net.InetSocketAddress;

public class OldHttpServer {
    public static void main(String[] args) throws Exception {
        HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
        server.createContext("/", exchange -> {
            String resp = "Hello World";
            exchange.sendResponseHeaders(200, resp.length());
            exchange.getResponseBody().write(resp.getBytes());
            exchange.close();
        });
        server.start();
    }
}

//java18
import com.sun.net.httpserver.HttpServer;
import java.net.InetSocketAddress;
import java.nio.file.*;

public class SimpleWebServerExample {
    public static void main(String[] args) throws Exception {
        var server = HttpServer.create(new InetSocketAddress(8000), 0);
        var handler = com.sun.net.httpserver.SimpleFileServer.createFileHandler(Path.of("."));
        server.createContext("/", handler);
        server.start();
        System.out.println("Server started at http://localhost:8000/");
    }
}
//启动后访问 http://localhost:8000/ 就能看到当前目录的文件

SimpleWebServer存在的意义并不是取代Java web(做网站),而是快速提供HTTP访问(例如
适合做 本地调试前端页面、快速共享文件、演示 HTML/CSS/JS)

  • 在Java18之前,如果你想通过HTTP提供一个目录下的文件,有几个选择

    1. Tomcat/Jetty——过于兴师动众
    2. 一个HttpServer 程序——多行代码实现
    3. Python/Node.js内置HTTP服务——非Java原生
  • 那么Java18添加了Simple Web Server后,相当于自己内置了一个HTTP服务(零代码,零依赖,跨平台一致)

    本质

    • 它就是一个超轻量级 HTTP 文件服务器
    • 没有 JSP、没有 Servlet、没有业务逻辑
    • 替代的不是 Tomcat,而是 python -m http.server 这类命令

UTF-8作为默认字符集

//java17及以前
// 在不同系统上,默认编码可能是 UTF-8(Linux)或 GBK(Windows)
System.out.println(Charset.defaultCharset()); // Windows 上可能输出 GBK

//java18
System.out.println(Charset.defaultCharset()); // 永远是 UTF-8,彻底消除跨平台编码差异

String API增强

//java17及以前
String str = "hello";
String upper = str.toUpperCase();
String indented = "line1\nline2".replace("\n", "\n    "); // 手动缩进

//java18
String result = "hello".transform(s -> s.toUpperCase()); // HELLO
String indented = "line1\nline2".indent(4); // 自动缩进
String escaped = "Hello\\nWorld".translateEscapes(); // 解析为换行

@snippet(JavaDoc 代码块)

//java17及以前
/**
 * 例子:
 * <pre>
 * System.out.println("Hello");
 * </pre>
 */

//java18
/**
 * 演示输出:
 * {@snippet :
 * System.out.println("Hello");
 * }
 */

代码可编译,可被 JavaDoc 工具直接验证

Java19

2022年9月推出的短期版本,为Java21(LTS)的出现做铺垫

Record Pattern

// Java 18 及之前
record Point(int x, int y) {}

Object obj = new Point(1, 2);
if (obj instanceof Point) {
    Point p = (Point) obj;
    int x = p.x();
    int y = p.y();
    System.out.println(x + ", " + y);
}

//java19更简洁
record Point(int x, int y) {}

Object obj = new Point(1, 2);
if (obj instanceof Point(int x, int y)) {
    System.out.println(x + ", " + y);
}

更好的配合 instanceofswitch 使用,可以直接解构 record 的字段。

Switch Pattern的进一步改进

//java18及以前
static String formatter(Object obj) {
    if (obj instanceof Integer i) {
        return "int " + i;
    } else if (obj instanceof String s) {
        return "string " + s;
    }
    return "unknown";
}

//java19
static String formatter(Object obj) {
    return switch (obj) {
        case String s && s.length() > 5 -> "Long string: " + s;
        case String s -> "Short string: " + s;
        case null -> "Null value";
        default -> obj.toString();
    };
}

可以看到其支持了更复杂的模式和null 匹配

Virtual Thread(虚拟线程)

  • Project Loom 的核心功能:虚拟线程,极大提升并发能力。
  • 虚拟线程是由 JVM 管理的轻量线程,可以创建百万级别的线程而不占用大量内存,其出现的意义是简化高并发编程,替代复杂的异步回调
//java18及以前,我们使用传统线程池,假设有1000 个任务需要大量系统线程,会占大量内存
ExecutorService executor = Executors.newFixedThreadPool(100);
for (int i = 0; i < 1000; i++) {
    executor.submit(() -> {
        Thread.sleep(1000);
        System.out.println(Thread.currentThread());
    });
}
executor.shutdown();

//java19 虚拟线程,每个任务一个虚拟线程,调度极轻量
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
for (int i = 0; i < 1000; i++) {
    executor.submit(() -> {
        Thread.sleep(1000);
        System.out.println(Thread.currentThread());
    });
}
executor.shutdown();

结构化并发

结构化并发是一种管理并发任务的 API,把多个任务组织成一个“作用域”,方便取消、等待和错误处理,该功能在java19处于孵化阶段,但结构化并发的出现对于管理复杂任务非常方便

try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Future<String> user  = scope.fork(() -> findUser());
    Future<Integer> order = scope.fork(() -> fetchOrderCount());
    scope.join();  // 等待所有
    scope.throwIfFailed(); // 如果任何失败则抛异常
    System.out.println(user.resultNow() + " - " + order.resultNow());
}

Java20

Java20是2023年3月发布的短期版本,该版本的主要作用依然是继续推动Project Loom(虚拟线程,结构化并发),模式匹配等特性的演进

Record Pattern V2

//java19及以前
record Point(int x, int y) {}
record Rectangle(Point topLeft, Point bottomRight) {}

static void print(Rectangle r) {
    if (r instanceof Rectangle(Point tl, Point br)) {
        System.out.println("TopLeft = " + tl + ", BottomRight = " + br);
    }
}

//java20,无须层层拆解
record Point(int x, int y) {}
record Rectangle(Point topLeft, Point bottomRight) {}

static void print(Rectangle r) {
    if (r instanceof Rectangle(Point(int x1, int y1), Point(int x2, int y2))) {
        System.out.println("From (" + x1 + "," + y1 + ") to (" + x2 + "," + y2 + ")");
    }
}

Switch模式匹配V4

之前不是改成了可以直接用lamada表达式嘛,但现在开启了模式守卫(guard),这个模式可以让你直接在case 中加条件

//java19及之前
static String format(Object obj) {
    return switch (obj) {
        case Integer i -> "int " + i;
        case String s  -> "string " + s;
        default        -> "unknown";
    };
}

//java20,有点像Kotlin/Scala这种现代模式匹配语言
static String format(Object obj) {
    return switch (obj) {
        case Integer i when i > 0 -> "positive int " + i;
        case Integer i            -> "non-positive int " + i;
        case String s             -> "string " + s;
        default                   -> "unknown";
    };
}

虚拟线程V2

主要是bug修复和行为细节改进,代码用法上和Java19基本相同

对了,这个东西我目前不建议使用,因为新开发的功能,似乎看起来不是非常稳定

//用法一致
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 1000; i++) {
        executor.submit(() -> {
            Thread.sleep(100);
            System.out.println(Thread.currentThread());
            return null;
        });
    }
}

java20的变化主要是

  • 内部调度更稳定,不容易死锁
  • ThreadLocal 更好兼容
  • 引入了 Scoped Values(JEP 429),作为 ThreadLocal 的替代方案

Java21(LTS)

java21是2023年9月发布的LTS版本,是Java17后的下一个长期支持版本,也是很多企业目前从java8升级的目标版本,Java21里对我们之前提到的很多孵化器和预览特性功能进行了正式落地

主要我们日常可以使用的还是之前提到的Switch模式匹配,Record模式,虚拟线程这些用法之前已经做了详细的对比和说明,不再赘述

说一下前面未提到的功能

字符串模板

这个真的是非常常用的特性,可以像Kotlin/JS一样的字符串插值

//java20及之前
String name = "Alice";
int age = 20;
String msg = String.format("Name: %s, Age: %d", name, age);

//java21
String name = "Alice";
int age = 20;
String msg = STR."Name: \{name}, Age: \{age}";
System.out.println(msg); // Name: Alice, Age: 20

还支持 SQL / JSON 模板,防止拼接注入问题

//java20之前
// 用户输入
String user = "Alice";
String pwd = "' OR '1'='1";

// 危险的拼接写法
String sql = "SELECT * FROM users WHERE name = '" + user + "' AND password = '" + pwd + "'";

System.out.println(sql);
// SELECT * FROM users WHERE name = 'Alice' AND password = '' OR '1'='1'

//java21
//SQL模板
import static java.lang.StringTemplate.RAW;

String user = "Alice";
String pwd = "' OR '1'='1";

// 假设有一个 SQL 模板处理器(通常由第三方库或 JDBC 驱动提供)
SQLProcessor SQL = new SQLProcessor();

StringTemplate query = RAW."SELECT * FROM users WHERE name = \{user} AND password = \{pwd}";
PreparedStatement ps = connection.prepareStatement(SQL.process(query));

// SQLProcessor 会自动把 \{user}, \{pwd} 转换为参数绑定(? 占位符)
// 例如:SELECT * FROM users WHERE name = ? AND password = ?
// 然后安全地 set 参数
//好处是 自动参数化,不需要自己 ps.setString(),直接规避 SQL 注入。

//json模板
import static java.lang.StringTemplate.STR;

String name = "Alice";
int age = 25;

// 通过 JSON 处理器自动生成合法 JSON
JsonTemplateProcessor JSON = new JsonTemplateProcessor();

StringTemplate jsonTpl = STR."""
{
  "name": "\{name}",
  "age": \{age}
}
""";

String json = JSON.process(jsonTpl);

System.out.println(json);
// {"name":"Alice","age":25}

无名模式 & 无名变量

这个模式可以减少“用不到的变量名”

//java20及以前
if (obj instanceof String s) {
    System.out.println("Got a string");
}

//java21
if (obj instanceof String _) {
    System.out.println("Got a string");
}
//_ 表示“占位符”,编译器知道类型,但你不需要变量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值