---
globs: ["**/*.java"]
---

# Java 后端编码规范

## 代码格式
- 左大括号前不换行，后换行；右大括号前换行
- `if/for/while/switch/do` 与括号之间必须加空格
- 二目、三目运算符左右两边必须加空格
- 注释双斜线与内容之间有且仅有一个空格
- 方法参数逗号后必须加空格
- 类型强制转换时右括号与值之间不加空格
- 超过 120 字符换行：第二行缩进 4 空格，运算符与下文一起换行，点号与下文一起换行

## 常量与变量
- 禁止任何魔法值直接出现在代码中，必须预定义常量
- `long/Long` 赋值后缀使用大写 `L`，禁止小写 `l`
- 浮点数后缀统一大写 `D` 或 `F`
- 按功能归类常量，禁止用一个常量类维护所有常量
- 固定范围的变量值使用 enum 定义

## OOP 规约
- 覆写方法必须加 `@Override` 注解
- 过时接口必须加 `@Deprecated` 注解并说明替代方案
- 禁止使用过时的类或方法
- 通过类名访问静态变量和方法，禁止通过实例访问
- 可变参数必须放在参数列表最后，类型禁止定义为 Object
- 外部正在调用的接口，禁止修改方法签名

## POJO 类规范
- POJO 属性必须使用包装数据类型，禁止基本类型
- RPC 方法的返回值和参数必须使用包装数据类型
- 局部变量推荐使用基本数据类型
- 布尔类型变量禁止加 `is` 前缀
- 禁止设定任何属性默认值
- 必须实现 `toString()` 方法，继承时加 `super.toString()`
- 禁止同时存在 `isXxx()` 和 `getXxx()` 方法
- 构造方法禁止加入业务逻辑，初始化逻辑放 `init` 方法
- getter/setter 中禁止增加业务逻辑
- 序列化类新增属性时，禁止修改 `serialVersionUID`
- DO 类属性类型必须与数据库字段类型匹配

## 数值与金额
- 货币金额必须使用 `BigDecimal` 类型
- 浮点数等值判断：禁止使用 `==` 和 `equals()`，使用 `BigDecimal.compareTo()`
- `BigDecimal` 等值比较使用 `compareTo()`，禁止 `equals()`
- 禁止 `new BigDecimal(double)`，使用 `new BigDecimal("0.1")` 或 `BigDecimal.valueOf(0.1)`
- 整型包装类比较全部使用 `equals()`，禁止 `==`
- equals 调用：常量或确定有值的对象在前，如 `"test".equals(param)` 或 `Objects.equals()`

## 日期时间
- 日期格式化 pattern 中年份使用小写 `yyyy`，禁止大写 `YYYY`
- 月份大写 `M`，分钟小写 `m`；24 小时大写 `H`，12 小时小写 `h`
- 获取毫秒数使用 `System.currentTimeMillis()`，禁止 `new Date().getTime()`
- 禁止硬编码一年为 365 天，使用 `LocalDate.lengthOfYear()`
- JDK8+ 推荐使用 `Instant`、`LocalDateTime`、`DateTimeFormatter`

## 集合处理
- 覆写 `equals` 必须同时覆写 `hashCode`
- 判空使用 `isEmpty()`，禁止 `size() == 0`
- `Collectors.toMap()` 必须提供 mergeFunction 参数处理 key 冲突
- `Collectors.toMap()` 注意 value 为 null 时会抛 NPE
- `subList` 返回的是视图，禁止强转为 ArrayList
- 禁止对 `keySet()/values()/entrySet()` 返回的集合做添加操作
- `Collections.emptyList()` 等返回的是不可变集合，禁止修改
- foreach 循环中禁止 remove/add 操作，使用 Iterator
- 集合转数组使用 `toArray(new String[0])`
- `addAll()` 前必须对输入集合做 NPE 判断
- `Arrays.asList()` 返回的集合禁止调用 add/remove/clear
- HashMap 初始化时指定初始容量，默认 16
- 遍历 Map 使用 `entrySet()` 或 `Map.forEach()`，禁止 `keySet()` 遍历取值
- Comparator 实现必须满足自反性、传递性、对称性

## 控制语句
- `if/else/for/while/do` 必须使用大括号，即使只有一行
- switch 每个 case 必须有 break/return/continue 或注释说明 fall-through
- switch 必须包含 default 语句
- switch 的 String 变量为外部参数时，必须先 null 判断
- if-else 不超过 3 层，超过时使用卫语句或策略模式
- 高并发场景禁止用"等于"作为中断条件，使用区间判断
- 三目运算符注意表达式类型不一致时的自动拆箱 NPE 风险

## 注释规约
- 类、类属性、类方法注释使用 Javadoc `/** */` 格式
- 所有抽象方法必须有 Javadoc 注释
- 所有类必须添加 `@author` 和 `@date`（格式 yyyy/MM/dd）
- 枚举类型字段必须有注释说明每个值的用途
- 方法内单行注释在被注释语句上方另起一行，使用 `//`
- 代码修改时同步更新注释

## 日志规范
- 使用 SLF4J 门面日志框架，禁止直接使用 Log4j/Logback API
- 日志字符串拼接使用占位符 `{}`，禁止字符串拼接
- trace/debug/info 级别日志必须先判断 `logger.isDebugEnabled()`
- 日志配置设置 `additivity=false` 避免重复打印
- 异常日志必须包含现场参数和堆栈：`logger.error("params: {} msg: {}", params, e.getMessage(), e)`
- 日志打印禁止用 JSON 工具将对象转 String，使用 `toString()`
- 生产环境禁止输出 debug 日志
- error 级别仅记录系统逻辑错误和重要异常
- 用户输入参数错误使用 warn 级别

## 其他
- 正则表达式必须预编译，禁止在方法体内定义 `Pattern.compile()`
- 禁止使用 `ApacheBeanUtils` 进行属性拷贝，使用 `SpringBeanUtils` 或 `CglibBeanCopier`
- 随机整数使用 `Random.nextInt()`，禁止 `Math.random()` 放大取整
- 数据结构初始化必须指定大小
- 类方法顺序：public/protected → private → getter/setter
- 循环体内字符串拼接使用 `StringBuilder.append()`

## 常见错误模式
- ❌ `Boolean isDeleted` → ✅ `Boolean deleted`（POJO 布尔字段禁止 is 前缀）
- ❌ `new BigDecimal(0.1)` → ✅ `new BigDecimal("0.1")`
- ❌ `param.equals("test")` → ✅ `"test".equals(param)` 或 `Objects.equals()`
- ❌ `for (String s : list) { list.remove(s); }` → ✅ 使用 `Iterator.remove()`
- ❌ `String key = "Id#company" + id;` → ✅ 预定义常量 `CACHE_KEY_PREFIX`
