Java版本新特性

Java8

lamdba表达式

Lambda 表达式是 Java 8 引入的简化语法,用来简化匿名内部类的写法,专门用于创建函数式接口的实例,让代码更简洁、更优雅。

一、什么是匿名内部类

匿名内部类 = 没有名字的局部内部类,一次性使用、当场定义、当场使用。

最简单的对比:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 正常写法
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Hello");
}
}
Runnable r = new MyRunnable();

// 匿名内部类写法:不用建类
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
};

二、什么是函数式接口

只有一个抽象方法的接口(default、static接口不用管),就是函数式接口。

Lambda 只能用在这种接口上!

三、Lambda 标准语法

1
(参数列表) -> { 方法体 }

极简规则(越写越短)

  1. 参数类型可以省略(编译器自动推断)
  2. 只有一个参数时,小括号可以省略
  3. 方法体只有一行代码时,大括号、return、分号都可以省略
1
2
3
4
5
6
7
8
9
10
// 旧写法:匿名内部类(啰嗦)
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程运行");
}
}).start();

// Lambda 写法(极简)
new Thread(() -> System.out.println("线程运行")).start();

例如最常用的forEach实现起来是这样的

1
2
3
4
5
6
7
8
9
10
11
12
List<BusiTextbook> list = busiTextbookService.list(queryWrapper);
// lamdba方式
list.forEach(item->{
item.setTimeStr(item.getTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
});

// 匿名函数方式
list.forEach(new Consumer<BusiTextbook>() {
@Override
public void accept(BusiTextbook busiTextbook) { busiTextbook.setTimeStr(busiTextbook.getTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
}
});

stream流

stream是对集合(List/Set)的操作简化工具

一、最常用的句式

1
2
3
1. 获取流 → list.stream()
2. 中间操作 → filter(过滤) / map(转换) / sorted(排序) / limit(截取) / distinct(去重)
3. 终结操作 → collect(收集) / forEach(遍历) / count(计数) / anyMatch(判断)

二、常用操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 找出年龄大于18的用户
List<User> adults = userList.stream()
.filter(user -> user.getAge() > 18)
.collect(Collectors.toList());

// 取出所有用户的名字 → 变成 List<String>
List<String> names = userList.stream()
.map(User::getName)
.collect(Collectors.toList());

// 按年龄从小到大
List<User> sortedList = userList.stream()
.sorted(Comparator.comparingInt(User::getAge))
// .sorted(Comparator.comparing(User::getName))
// 降序排序
// .sorted(Comparator.comparing(User::getName).reversed())
.collect(Collectors.toList());

// 截取前五个
list.stream()
.limit(0,5)
.collect(Collectors.toList());

// 列表去重
list.stream()
.distinct()
.collect(Collectors.toList());

// 收集
List<User> sortedList = userList.stream()
.sorted()
.collect(Collectors.toList());
/* .collect(Collectors.toSet()); */
/* .collect(Collectors.toMap(s -> s, s -> s)); */

// 遍历
userList.forEach(System.out::println);

// 计数
long count = userList.stream()
.filter(u -> u.getAge() > 18)
.count();

// 判断是否存在满足条件的
boolean hasAdult = userList.stream()
.anyMatch(u -> u.getAge() > 18);

Optional 类

避免空指针异常 NullPointerException

实例:

1
2
3
4
5
6
7
8
9
10
11
12
// 以前的判空
User user = getUserById(id);
if (user != null) {
System.out.println(user.getName());
}

// 现在的判空
Optional<User> userOpt = getUserById(id);
/** 有值就执行没值就拉倒 **/
userOpt.ifPresent(user -> System.out.println(user.getName()));
/** 没值的时候给定默认值 **/
User user = userOpt.orElseGet(() -> new User())

接口默认方法和静态方法

静态方法特点

  1. 方法体
  2. static 修饰
  3. 属于接口自己,必须接口名调用
  4. 不能被实现类继承
  5. 不能被重写

默认方法特点:

  1. 方法体
  2. default 修饰
  3. 属于实例方法,必须对象调用
  4. 实现类可以不重写,直接继承使用
  5. 实现类也可以重写覆盖它
1
2
3
4
5
6
7
8
9
10
11
public interface MyInterface {
// 静态方法
static void staticMethod() {
System.out.println("接口里的静态方法");
}

// 默认方法
default void hello() {
System.out.println("我是默认方法");
}
}

Java11

String 类新增 4 个实用方法

  1. isBlank () → 判断是否为空

    1
    2
    String str = "   ";
    str.isBlank(); // true

    空格、空串、tab、换行 → 都算空

  2. strip () → 去除空白

    1
    2
    String str = "  你好  ";
    str.strip(); // "你好"

    能去除 全角空格、Unicode 空白字符

  3. repeat (n) → 重复字符串

    1
    "*".repeat(10); // **********
  4. lines () → 按行分割成流

    1
    2
    String str = "第一行\n第二行\n第三行";
    str.lines().forEach(System.out::println);

局部变量类型推断(var)

var 代替局部变量的具体类型,编译器自动推断类型

一、用法

传统写法:

1
2
String name = "张三";
List<User> list = new ArrayList<>();

var写法

1
2
var name = "张三";     // 编译器推断为 String
var list = new ArrayList<User>(); // 推断为 ArrayList<User>

增强for循环

1
2
3
for (var item : list) {
System.out.println(item);
}

try资源关闭

1
2
3
try (var is = new FileInputStream("a.txt")) {
// 读写
}

二、规则

  1. var 只能用在局部变量

  2. 必须当场初始化,不能只声明不赋值

  3. 推断后类型就固定,不能再乱赋值别的类型

  4. 匿名子类、复杂泛型 也能自动推断,简化代码

ZGC垃圾回收器

一、特点

  1. 低延迟天花板

    STW 停顿基本 1ms 左右,用户几乎感知不到卡顿。

  2. 堆再大也不卡

    G1、堆越大停顿越久;ZGC 堆大小和停顿时间几乎没关系

  3. 全程大部分并发

    只有两次极短暂停顿,其余回收全程和业务线程一起跑。

  4. 自带内存整理,几乎无内存碎片

二、核心技术

  1. 染色指针:64 位指针里存 GC 标记状态,不用频繁扫对象
  2. 读屏障:访问对象自动修复引用,不卡顿
  3. 多重虚拟内存映射:实现对象并发搬家不阻塞业务

Java17

records

Record 是 Java 原生极简数据载体类,专门用来做:DTO、实体、值对象、只读数据传输

1
2
3
4
5
6
7
// 三个参数
record User(Long id, String name, Integer age) {
// 自定义普通方法
public String info() {
return name + "-" + age;
}
}

等价于你手写一整个标准 POJO

  • 私有 final 成员变量
  • 全参构造器
  • 自动生成 id()name()age() 访问方法(不是 getId!是 id ())
  • 自动重写 equals ()、hashCode ()、toString ()
  • 添加一个自定义方法

密封类

精确控制谁能继承我、谁能实现我

不再是普通类随便谁都能 extends / implements

父类可以指定允许的子类名单,没在名单里的直接编译报错。

关键词:

sealed(密封)、permits(允许)、non-sealed(非密封)

  1. 密封父类 + 指定允许子类

    1
    2
    3
    // 密封类:只允许 Circle、Rectangle 继承
    public sealed class Shape permits Circle, Rectangle {
    }
  2. 被允许的子类 只能三种写法

    final 最终类,不能再被继承

    sealed 继续密封,再限制下一级子类

    non-sealed 解封,放开随便继承

    写法 1:子类加 final(常用)

    1
    2
    3
    // 不能再被别人继承
    public final class Circle extends Shape {
    }
    1
    2
    public final class Rectangle extends Shape {
    }

    写法 2:子类继续 sealed

    1
    2
    3
    4
    5
    public sealed class Triangle extends Shape permits RightTriangle {
    }

    public final class RightTriangle extends Triangle {
    }

    写法 3:子类 non-sealed 放开继承

    1
    2
    3
    // 解封:任何人都可以继承我
    public non-sealed class Square extends Shape {
    }

模式匹配instanceof

1
2
3
4
5
6
7
Object obj = "我是字符串";

// 直接声明变量 s,匹配成功后自动强转
if (obj instanceof String s) {
// 无需强转,直接用!
System.out.println(s.length());
}

Java21

虚拟线程

虚拟线程是 JVM 实现的用户态轻量级线程,廉价、海量、不用线程池

  • 传统线程:平台线程(Platform Thread),1:1 映射操作系统内核线程,成本高、数量有限
  • 虚拟线程:由 JVM 调度管理,不直接绑定 OS 内核线程,极轻量、可百万级并发

springboot整合虚拟线程

1
2
3
4
5
6
7
8
/** 创建虚拟线程配置文件 */
@Configuration
public class VtConfig {
@Bean
public ExecutorService vtExecutor() {
return Executors.newVirtualThreadPerTaskExecutor();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class FileService {
@Autowired
private ExecutorService vtExecutor;

public void batchProcess(Path dirPath) throws IOException {
Files.list(dirPath).forEach(file -> {
vtExecutor.submit(() -> handleSingleFile(file));
});
}

private void handleSingleFile(Path path) {
// 处理逻辑
}
}

switch类型判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 🔥 Java 21 模式匹配 switch:判断类型
static void testType(Object obj) {
String typeName = switch (obj) {
case String s -> "字符串 String";
case Integer i -> "整数 Integer";
case Double d -> "浮点数 Double";
case Boolean b -> "布尔 Boolean";
case int[] arr -> "int 数组";
case null -> "null 值";
default -> "其他类型:" + obj.getClass().getName();
};

System.out.println("类型 = " + typeName);
}