Java 21 特性规范
# Java 21 特性规范
本规则补充 Java 21 版本特性使用方式与注意事项,通用语言规范见 8.java-language-specification.mdc
# Record 类
- 使用 Record 表达不可变数据传输对象
- 优先用于值对象和返回结果封装
public record UserInfo(String name, String email, LocalDateTime createdAt) {}
1
# Pattern Matching
- 在 switch 表达式中使用模式匹配提升分支可读性
- 保持各分支覆盖清晰,使用默认分支兜底
public String formatValue(Object value) {
return switch (value) {
case String s -> "String: " + s;
case Integer i -> "Number: " + i;
case null -> "null value";
default -> "Unknown: " + value.toString();
};
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# Text Blocks
- 使用 Text Blocks 表达多行字符串,适用于 SQL 与 JSON 内容
- 保持缩进与排版清晰,避免在文本中混入业务逻辑
String sql = """
SELECT u.name, u.email
FROM users u
WHERE u.status = 'ACTIVE'
ORDER BY u.created_at DESC
""";
1
2
3
4
5
6
2
3
4
5
6
# Sealed Classes
- 使用密封类构建受限类层次结构,明确允许的子类范围
- 结合模式匹配简化结果分支处理
public sealed class Result<T> permits Success, Error {
// 基类定义
}
public final class Success<T> extends Result<T> {
private final T data;
// 实现
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# Virtual Threads
- 在高并发场景中优先评估虚拟线程以提升资源利用率
- 避免在虚拟线程内使用阻塞式长时间占用操作
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
// 高并发任务
});
}
1
2
3
4
5
2
3
4
5
# description: Java 约定与最佳实践(命名、风格、异常、集合、并发、测试等) globs: "**/*.java" alwaysApply: false
# Java 语言规范
# Java 21 特性使用
Record类:用于不可变数据传输对象
public record UserInfo(String name, String email, LocalDateTime createdAt) {}1Pattern Matching:在switch表达式中使用模式匹配
public String formatValue(Object value) { return switch (value) { case String s -> "String: " + s; case Integer i -> "Number: " + i; case null -> "null value"; default -> "Unknown: " + value.toString(); }; }1
2
3
4
5
6
7
8Text Blocks:用于多行字符串,特别是SQL和JSON
String sql = """ SELECT u.name, u.email FROM users u WHERE u.status = 'ACTIVE' ORDER BY u.created_at DESC """;1
2
3
4
5
6Sealed Classes:用于受限的类层次结构
public sealed class Result<T> permits Success, Error { // 基类定义 } public final class Success<T> extends Result<T> { private final T data; // 实现 }1
2
3
4
5
6
7
8Virtual Threads:用于高并发场景
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { executor.submit(() -> { // 高并发任务 }); }1
2
3
4
5
# 命名约定
- 类名:使用帕斯卡命名法(如
UserController、OrderService) - 方法和变量名:使用驼峰命名法(如
findUserById、isOrderValid) - 常量:使用全大写下划线分隔(如
MAX_RETRY_ATTEMPTS、DEFAULT_PAGE_SIZE) - 包名:使用小写,按功能模块划分(如
com.example.user.domain)
# 代码风格
- 缩进:使用4个空格,不使用Tab
- 行长度:每行不超过120个字符
- 大括号:使用Egyptian风格(开括号不换行)
- 空行:方法间使用一个空行分隔,逻辑块间使用空行分隔
# 比较与空安全
- 静态/常量在左:静态数据或变量与可能为 null 的变量比较时,应使用静态变量或明确的非 null 值在左侧调用
equals,避免空指针异常// 推荐:字面量/常量在左 "streakRestart".equals(param.getIntention()) AdTypeEnum.REWARD_VIDEO.getCode().equals(param.getAdSourceType()) // 不推荐:可能为 null 的变量在左,param.getIntention() 为 null 时会 NPE param.getIntention().equals("streakRestart") param.getAdSourceType().equals(AdTypeEnum.REWARD_VIDEO.getCode())1
2
3
4
5
6
7
# 异常处理
检查异常:谨慎使用检查异常,优先使用运行时异常
异常链:保持异常链,不丢失原始异常信息
try { // 可能抛出异常的代码 } catch (SpecificException e) { throw new BusinessException("业务处理失败", e); }1
2
3
4
5资源管理:使用try-with-resources自动管理资源
try (var reader = Files.newBufferedReader(path)) { // 使用reader }1
2
3
# 集合和流处理
集合选择:根据使用场景选择合适的集合类型
ArrayList:随机访问频繁LinkedList:插入删除频繁HashMap:键值对存储TreeMap:需要排序的键值对
Stream API:充分利用Stream API进行函数式编程
List<String> activeUserNames = users.stream() .filter(user -> user.isActive()) .map(User::getName) .sorted() .toList();1
2
3
4
5
# 并发编程
- 线程安全:优先使用不可变对象和线程安全的集合
- 锁机制:合理使用synchronized、ReentrantLock等锁机制
- 并发集合:使用ConcurrentHashMap、CopyOnWriteArrayList等并发集合
- CompletableFuture:使用CompletableFuture处理异步操作
CompletableFuture<String> future = CompletableFuture .supplyAsync(() -> fetchData()) .thenApply(data -> processData(data)) .exceptionally(throwable -> "默认值");1
2
3
4
# 内存管理
- 对象创建:避免在循环中创建不必要的对象
- 字符串处理:大量字符串操作使用StringBuilder
- 集合大小:预估集合大小,避免频繁扩容
- 弱引用:适当使用WeakReference避免内存泄漏
# 泛型使用
- 类型安全:充分利用泛型提供类型安全
- 通配符:正确使用上界通配符(? extends)和下界通配符(? super)
- 类型擦除:理解泛型类型擦除的限制
public <T extends Comparable<T>> T findMax(List<T> list) { return list.stream().max(Comparable::compareTo).orElse(null); }1
2
3
# 注解使用
- 标准注解:正确使用@Override、@Deprecated、@SuppressWarnings等
- 自定义注解:合理创建自定义注解简化代码
- 注解处理:了解编译时和运行时注解处理
# 测试规范
- 单元测试:使用JUnit 5编写单元测试
- 测试命名:测试方法使用描述性命名(如
shouldReturnUserWhenValidIdProvided) - 断言:使用AssertJ提供更好的断言体验
@Test void shouldCalculateCorrectTotal() { // Given List<Item> items = List.of( new Item("item1", 10.0), new Item("item2", 20.0) ); // When double total = calculator.calculateTotal(items); // Then assertThat(total).isEqualTo(30.0); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 性能优化
- 算法复杂度:选择合适的算法和数据结构
- 缓存策略:合理使用缓存减少重复计算
- 懒加载:对于昂贵的操作使用懒加载
- 批量处理:批量处理数据库操作和网络请求
# 代码质量
- 单一职责:每个类和方法只负责一个功能
- 开闭原则:对扩展开放,对修改关闭
- 依赖倒置:依赖抽象而不是具体实现
- 接口隔离:使用小而专一的接口
- 代码复用:提取公共逻辑,避免重复代码
# 文档和注释
- JavaDoc:为公共API编写完整的JavaDoc
- 代码注释:为复杂逻辑添加解释性注释,强制使用中文进行注释
- 方法体注释:方法体中应根据逻辑适当增加中文注释,提高代码可阅读性(关键分支、非显而易见逻辑、业务含义等建议加注释)
- TODO标记:使用TODO标记待完成的工作
/** * 计算用户积分 * * @param userId 用户ID * @param actions 用户行为列表 * @return 计算得出的积分值 * @throws UserNotFoundException 当用户不存在时抛出 */ public int calculatePoints(Long userId, List<UserAction> actions) { // TODO: 实现积分计算逻辑 return 0; }1
2
3
4
5
6
7
8
9
10
11
12
# 合理分类
当为统一功能或相似配置创建多个关联类时,根据定义将相关类文件整理到合理结构包下。