Java编程代码规范指南
# Java编程代码规范指南
# 目录介绍
- 01.代码规范概述
- 02.命名规范设计
- 03.代码格式规范
- 04.注释规范设计
- 05.编程实践规范
- 06.面向对象规范
- 07.跨语言对比
- 08.日志规范
- 09.控制语句规范
- 10.日期与时间规范
- 11.equals与hashCode
- 12.序列化规范
- 13.枚举使用规范
- 14.数组与泛型补充
- 15.MySQL数据库规范
- 16.单元测试规范
- 17.安全规约
- 18.工程结构规范
- 19.Stream API 规范
- 20.Lombok 使用规范
- 21.性能优化规范
- 22.工具链与自动化规范
- 23.代码审查清单
- 24.常见陷阱速查
# 01.代码规范概述
# 1.1 为何需要代码规范
疑惑:代码能跑就行,为什么还要花时间制定和遵守规范?
答疑:代码的生命周期中,阅读时间远大于编写时间。据统计,开发者花在阅读代码上的时间是写代码的10倍以上。代码规范的本质是降低团队协作的认知成本。
论证——没有规范的代价:
开发者A的风格 开发者B的风格
──────────────── ────────────────
public void getData(){ public void get_data()
if(flag==true){ {
return result; if (flag)
} {
} return result;
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
当两种风格混合在同一个项目中:
- 代码审查效率降低:关注点从逻辑转移到格式
- Bug隐蔽性增加:不一致的风格容易掩盖逻辑错误
- 新人上手成本增加:需要适应多种风格
结果:Google、阿里、华为等大厂都有严格的编码规范,且通过工具强制执行。
# 1.2 规范的核心目标
| 目标 | 说明 |
|---|---|
| 可读性 | 代码像散文一样流畅,一目了然 |
| 一致性 | 整个项目像一个人写的 |
| 可维护性 | 半年后自己还能看懂 |
| 健壮性 | 从编码层面减少Bug |
| 可审查性 | Code Review 聚焦逻辑而非格式 |
# 1.3 规范的演变历史
1996 Sun Microsystems 发布《Java Code Conventions》—— 第一份官方规范
|
2009 Google 发布《Google Java Style Guide》—— 工业界最广泛采用
|
2017 阿里巴巴发布《Java开发手册》(孤尽版) —— 国内影响力最大
|
2018 Oracle 更新 Java SE 编码风格 —— 适配 Java 8+ 新特性
|
2023 阿里发布《Java开发手册》黄山版 —— 适配 Java 17+、Record、sealed
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
核心参考标准:
- Google Java Style Guide
- 阿里巴巴Java开发手册
- Oracle Code Conventions for the Java Programming Language
- Effective Java(Joshua Bloch)
# 02.命名规范设计
# 2.1 包名命名规范
核心原则:全部小写,使用反域名命名法,层次分明。
// 正确示例
com.company.project.module.submodule
com.example.app.feature.login
com.example.app.data.repository
// 错误示例
com.Company.Project // 不能有大写
com.example.app.my_module // 不能有下划线
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
包结构设计建议:
com.example.app/
├── data/ // 数据层
│ ├── local/ // 本地数据源
│ ├── remote/ // 远程数据源
│ └── repository/ // 数据仓库
├── domain/ // 领域层
│ ├── model/ // 领域模型
│ └── usecase/ // 用例
├── presentation/ // 表现层
│ ├── view/ // 视图
│ └── viewmodel/ // ViewModel
└── utils/ // 工具类
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 2.2 类名命名规范
核心原则:大驼峰命名法(UpperCamelCase),名词或名词短语。
| 类型 | 命名规则 | 示例 |
|---|---|---|
| 普通类 | 名词,大驼峰 | UserManager, OrderService |
| 抽象类 | Abstract 或 Base 前缀 | AbstractParser, BaseActivity |
| 接口 | 形容词/能力词,或 I 前缀 | Serializable, ICallback |
| 异常类 | Exception 后缀 | BusinessException, DataNotFoundException |
| 测试类 | 被测类名 + Test | UserManagerTest |
| 工具类 | Utils 或 Helper 后缀 | StringUtils, DateHelper |
| 枚举类 | 名词,大驼峰 | OrderStatus, ColorType |
// 正确
public class HttpClientFactory { }
public abstract class AbstractDataSource { }
public interface OnClickListener { }
// 错误
public class httpClient { } // 首字母未大写
public class Manage_User { } // 不能有下划线
public class Info { } // 命名过于模糊
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 2.3 方法命名规范
核心原则:小驼峰命名法(lowerCamelCase),动词或动词短语。
// 获取类方法:get/find/query/fetch
public User getUserById(long id);
public List<Order> findOrdersByDate(Date date);
// 判断类方法:is/has/can/should
public boolean isEmpty();
public boolean hasPermission(String role);
public boolean canRetry();
// 设置类方法:set/update/modify
public void setName(String name);
public void updateProfile(Profile profile);
// 转换类方法:to/from/parse/format
public String toString();
public Order fromJson(String json);
// 回调类方法:on/handle/process
public void onClick(View view);
public void handleMessage(Message msg);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 2.4 变量命名规范
// 局部变量:小驼峰,有意义的名称
int userCount = 0; // 正确
int cnt = 0; // 不推荐,缩写含义不清
// 成员变量:小驼峰(Android中常用 m 前缀)
private String mUserName; // Android 风格
private String userName; // 标准 Java 风格
// 布尔变量:is/has/can 前缀
private boolean isActive;
private boolean hasData;
// 集合变量:复数或集合类型后缀
private List<User> users;
private Map<String, Object> configMap;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
严禁使用的命名方式:
// 单字母变量(循环索引除外)
int a, b, c; // 错误
// 拼音命名
String dianHua; // 错误,应该用 phoneNumber
String shouJiHao; // 错误
// 数字编号
String value1, value2; // 错误,无法表达含义
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 2.5 常量命名规范
核心原则:全大写,下划线分隔。
// 正确
public static final int MAX_RETRY_COUNT = 3;
public static final String DEFAULT_CHARSET = "UTF-8";
public static final long CACHE_EXPIRE_TIME = 24 * 60 * 60 * 1000L;
// 错误
public static final int maxRetryCount = 3; // 未全大写
public static final int MAX = 3; // 命名过短,含义不清
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 2.6 命名反模式
| 反模式 | 示例 | 问题 | 改进 |
|---|---|---|---|
| 过度缩写 | usrMgr | 可读性差 | userManager |
| 含义不清 | data, info, temp | 无法理解用途 | userData, orderInfo |
| 类型冗余 | nameString, ageInt | 类型已知 | name, age |
| 否定命名 | isNotEmpty | 双重否定易混淆 | isEmpty 反转 |
| 过长命名 | getAllActiveUsersFromDatabaseByStatus | 太冗长 | findActiveUsersByStatus |
# 03.代码格式规范
# 3.1 缩进与空格
// 使用4个空格缩进(不使用Tab)
public void processData(List<String> items) {
for (String item : items) {
if (item != null) {
doSomething(item);
}
}
}
// 运算符两侧加空格
int result = a + b * c;
boolean isValid = (age >= 18) && (name != null);
// 逗号后加空格
public void init(String name, int age, boolean active) { }
// 类型转换后加空格
String text = (String) obj;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 3.2 换行与对齐
// 方法参数过长时换行
public void sendNotification(
String userId,
String title,
String content,
NotificationType type) {
// ...
}
// 链式调用换行
List<String> result = users.stream()
.filter(u -> u.isActive())
.map(User::getName)
.sorted()
.collect(Collectors.toList());
// 条件表达式过长时换行
if (userStatus == Status.ACTIVE
&& userRole == Role.ADMIN
&& hasPermission(Permission.WRITE)) {
// ...
}
// 单行长度不超过120个字符(Google规范)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 3.3 大括号风格
Java统一采用 K&R风格(Egyptian Brackets):
// 正确:左大括号不换行
public class UserService {
public void process() {
if (condition) {
doSomething();
} else {
doOther();
}
}
}
// 错误:左大括号换行(C#风格,Java不推荐)
public class UserService
{
public void process()
{
// ...
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
特殊情况:空方法体可以简写
public void onPause() { }
1
# 3.4 空行与分隔
public class UserService {
// 成员变量之间不需要空行(同组)
private final UserRepository userRepo;
private final CacheManager cache;
// 不同逻辑组之间用一个空行分隔
private static final int MAX_RETRY = 3;
// 构造方法与成员变量之间空一行
public UserService(UserRepository repo, CacheManager cache) {
this.userRepo = repo;
this.cache = cache;
}
// 方法之间空一行
public User getUser(long id) {
// 方法内逻辑分组用空行
User cached = cache.get(id);
if (cached != null) {
return cached;
}
// 不同逻辑块之间空一行
User user = userRepo.findById(id);
cache.put(id, user);
return user;
}
public void deleteUser(long id) {
// ...
}
}
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
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
# 04.注释规范设计
# 4.1 注释的核心原则
好的代码是自文档化的,注释是用来解释"为什么",而不是"做了什么"。
// 差的注释:复述代码
i++; // i加1
// 好的注释:解释意图
i++; // 跳过CSV文件的标题行
// 差的注释:过时信息
// 调用支付接口(注:已改为异步调用) ← 什么时候改的?现在还是异步吗?
processPayment();
// 好的注释:记录决策原因
// 使用重试机制,因为第三方支付接口在高峰期偶尔超时(2020-03确认的问题)
processPaymentWithRetry(MAX_RETRY);
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 4.2 类注释规范
/**
* 用户服务类,负责用户相关的核心业务逻辑。
*
* <p>主要职责包括:
* <ul>
* <li>用户信息的CRUD操作</li>
* <li>用户认证与授权</li>
* <li>用户数据缓存管理</li>
* </ul>
*
* <p>线程安全性:该类是线程安全的,内部通过 {@code synchronized} 保证并发访问安全。
*
* @author yc
* @since 1.0
*/
public class UserService {
// ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 4.3 方法注释规范
/**
* 根据用户ID查询用户信息。
*
* <p>优先从缓存中获取,缓存未命中时查询数据库。
* 查询结果会自动写入缓存,缓存过期时间为 {@value #CACHE_TTL} 秒。
*
* @param userId 用户唯一标识,不能为null
* @return 用户对象,如果用户不存在则返回null
* @throws IllegalArgumentException 如果userId为null
* @throws DataAccessException 如果数据库访问异常
*/
public User getUserById(@NonNull Long userId) {
// ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 4.4 行内注释规范
// 1. 行内注释放在代码上方,而非行尾(长注释场景)
// 使用双重检查锁实现单例,volatile防止指令重排
private volatile static Singleton instance;
// 2. 短注释可以放在行尾
int timeout = 30; // 单位:秒
// 3. 禁止无意义注释
String name; // 定义name变量 ← 这种注释删掉
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 4.5 TODO和FIXME
// TODO 标记待完成的功能
// TODO(yc): 添加分页查询支持,预计2.0版本实现
public List<User> getAllUsers() {
return userRepo.findAll(); // 当前无分页
}
// FIXME 标记已知Bug
// FIXME(yc): 当userId为Long.MAX_VALUE时会溢出,需要增加边界检查
public long calculateHash(long userId) {
return userId * 31 + offset;
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 05.编程实践规范
# 5.1 异常处理规范
// 1. 不要捕获所有异常
// 错误
try {
process();
} catch (Exception e) {
e.printStackTrace(); // 吞掉异常,生产环境看不到
}
// 正确
try {
process();
} catch (IOException e) {
logger.error("处理数据失败, file={}", filePath, e);
throw new BusinessException("数据处理失败", e);
}
// 2. finally 中不要使用 return
// 错误:finally中的return会覆盖try中的return
try {
return computeResult();
} finally {
return defaultValue; // 永远返回这个值!
}
// 3. 使用 try-with-resources(Java 7+)
// 正确
try (InputStream is = new FileInputStream(file);
BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
return reader.readLine();
}
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
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
# 5.2 空指针防御
// 1. 使用 Optional(Java 8+)
public Optional<User> findUser(long id) {
return Optional.ofNullable(userRepo.findById(id));
}
// 调用方
String name = findUser(id)
.map(User::getName)
.orElse("未知用户");
// 2. 方法参数校验
public void updateUser(@NonNull User user) {
Objects.requireNonNull(user, "user不能为null");
Objects.requireNonNull(user.getName(), "用户名不能为null");
// ...
}
// 3. 集合返回空集合而非null
public List<Order> getOrders(long userId) {
List<Order> orders = orderRepo.findByUserId(userId);
return orders != null ? orders : Collections.emptyList();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 5.3 集合使用规范
// 1. 指定集合初始容量(避免多次扩容)
// 已知大小时指定容量
List<User> users = new ArrayList<>(expectedSize);
Map<String, Object> map = new HashMap<>(16); // 初始容量为2的幂
// 2. 使用 isEmpty() 而非 size() == 0
if (list.isEmpty()) { } // 正确:O(1)
if (list.size() == 0) { } // 不推荐:某些集合size()是O(n)
// 3. 遍历时不要修改集合
// 错误:ConcurrentModificationException
for (String item : list) {
if (item.equals("remove")) {
list.remove(item);
}
}
// 正确:使用 Iterator 或 removeIf
list.removeIf(item -> item.equals("remove"));
// 4. 不可变集合
List<String> immutable = List.of("a", "b", "c"); // Java 9+
List<String> immutable2 = Collections.unmodifiableList(list); // Java 8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 5.4 字符串处理规范
// 1. 字符串比较使用 equals,常量放前面
// 正确:避免NPE
if ("active".equals(status)) { }
// 错误:status为null时NPE
if (status.equals("active")) { }
// 2. 字符串拼接
// 少量拼接:直接用 +(编译器自动优化)
String msg = "Hello, " + name + "!";
// 循环中拼接:使用 StringBuilder
StringBuilder sb = new StringBuilder(256);
for (String item : items) {
sb.append(item).append(",");
}
// 3. 字符串格式化
String msg = String.format("用户%s的订单数:%d", userName, orderCount);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 5.5 并发编程规范
// 1. 线程池不允许用 Executors 创建(阿里规约)
// 错误:无界队列可能OOM
ExecutorService pool = Executors.newFixedThreadPool(10);
// 正确:手动创建,明确参数
ExecutorService pool = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, TimeUnit.SECONDS, // 空闲超时
new LinkedBlockingQueue<>(1000), // 有界队列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 2. 使用 volatile 保证可见性
private volatile boolean running = true;
// 3. 使用 ConcurrentHashMap 而非 Collections.synchronizedMap
Map<String, Object> map = new ConcurrentHashMap<>();
// 4. 加锁顺序一致,避免死锁
// 始终按照相同的顺序获取锁
synchronized (lockA) {
synchronized (lockB) {
// ...
}
}
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
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
# 5.6 防御性拷贝 【推荐】
// ✅ 构造时对可变参数做防御性拷贝
public class Period {
private final Date start;
private final Date end;
public Period(Date start, Date end) {
// ❌ 直接赋值——外部仍持有引用,可以修改
// this.start = start;
// ✅ 防御性拷贝
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if (this.start.compareTo(this.end) > 0) {
throw new IllegalArgumentException("start after end");
}
}
// ✅ getter 也返回拷贝,防止外部修改内部状态
public Date getStart() {
return new Date(start.getTime());
}
}
// ✅ 集合字段同理——不直接暴露内部集合
public class OrderList {
private final List<Order> orders = new ArrayList<>();
// ✅ 返回不可修改视图
public List<Order> getOrders() {
return Collections.unmodifiableList(orders);
}
// ✅ 或用 List.copyOf(Java 10+,真不可变)
public List<Order> getOrdersCopy() {
return List.copyOf(orders);
}
}
// ❌ 直接暴露内部集合——外部可以随意增删
public List<Order> getOrders() {
return orders; // 危险!
}
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
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
防御性拷贝适用场景:
- 构造器接收可变参数(Date、Collection、数组等)
- getter 返回可变内部状态
- 跨层传递对象(Controller → Service → DAO)
- 注意:不可变类型(String、基本类型包装类、LocalDate)不需要拷贝
# 06.面向对象规范
# 6.1 类设计规范
// 1. 类的成员排列顺序
public class UserService {
// ① 静态常量
private static final String TAG = "UserService";
// ② 静态变量
private static UserService instance;
// ③ 成员变量
private final UserRepository userRepo;
private String currentUser;
// ④ 构造方法
public UserService(UserRepository repo) {
this.userRepo = repo;
}
// ⑤ 公有方法
public User getUser(long id) { }
// ⑥ 私有方法
private void validate(User user) { }
// ⑦ 内部类 / 内部接口
private static class CacheEntry { }
}
// 2. 类的行数建议不超过500行
// 3. 方法的行数建议不超过80行
// 4. 方法参数不超过5个,超过则封装为对象
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
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
# 6.2 接口设计规范
// 1. 接口方法默认 public abstract,不需要显式声明
public interface UserCallback {
void onSuccess(User user); // 正确
public abstract void onError(); // 冗余
}
// 2. 接口常量默认 public static final
public interface Constants {
int MAX_SIZE = 100; // 等同于 public static final int MAX_SIZE = 100;
}
// 3. Java 8+ 可以有 default 方法
public interface Logger {
void log(String message);
default void debug(String message) {
log("[DEBUG] " + message);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 6.3 继承与组合
// 优先使用组合而非继承
// 不推荐:继承导致强耦合
public class OrderService extends BaseService { }
// 推荐:组合更灵活
public class OrderService {
private final BaseService baseService;
public OrderService(BaseService baseService) {
this.baseService = baseService;
}
}
// 继承的合理场景:IS-A 关系且需要多态
public abstract class Shape {
public abstract double area();
}
public class Circle extends Shape {
@Override
public double area() { return Math.PI * r * r; }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 6.4 方法设计规范
// 1. 方法应该只做一件事
// 错误:一个方法做了太多事
public void processOrder(Order order) {
validate(order);
calculatePrice(order);
deductStock(order);
sendNotification(order);
updateStatistics(order);
}
// 方法体过大时应拆分子方法
// 2. 返回值语义明确
// 错误:用 -1 表示未找到
public int findIndex(String key) {
return -1; // 魔法数字
}
// 正确:使用 Optional 或定义常量
public OptionalInt findIndex(String key) {
return OptionalInt.empty();
}
// 3. 方法参数使用 final(防止意外修改)
public String format(final String input) {
// input 不会被意外重新赋值
return input.trim().toLowerCase();
}
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
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
# 6.5 Record 类(Java 14+)【推荐】
// ✅ 纯数据载体:用 Record 替代传统 POJO
public record UserDTO(Long id, String name, String email) {
// 自动生成:构造器、equals、hashCode、toString、getter
// 字段不可变(final),无 setter
}
// 等效的传统写法(约 60 行):
// public class UserDTO {
// private final Long id; ...
// public UserDTO(Long id, String name, String email) { ... }
// public Long getId() { return id; }
// public boolean equals(Object o) { ... } // 10+ 行
// public int hashCode() { ... } // 5+ 行
// public String toString() { ... } // 5+ 行
// }
// ✅ Record 可以加紧凑构造器做参数校验
public record PositivePoint(int x, int y) {
public PositivePoint {
if (x < 0 || y < 0) {
throw new IllegalArgumentException("坐标不能为负");
}
}
}
// ✅ Record 适合场景:API 返回对象、配置对象、事件消息
// ❌ Record 不适合:JPA Entity(需要可变性)、复杂业务对象(继承体系)
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
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
# 6.6 Sealed Classes(Java 17+)【推荐】
// ✅ 明确限定子类范围——禁止外部随意扩展
public sealed class Payment
permits CardPayment, WechatPayment, AliPayment {
}
public final class CardPayment extends Payment { }
public final class WechatPayment extends Payment { }
public sealed class AliPayment extends Payment // 可以继续 sealed
permits AliH5Payment, AliAppPayment { }
// ✅ 配合 switch 表达式,编译器检查全覆盖
double calcFee(Payment p) {
return switch (p) {
case CardPayment c -> c.amount() * 0.01;
case WechatPayment w -> w.amount() * 0.006;
case AliPayment a -> a.amount() * 0.007;
// 不需要 default——编译器强制全覆盖
};
}
// ❌ 传统写法无法限制子类——谁都能 extends
public class Payment { } // 任意包都可以子类化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 6.7 Pattern Matching(Java 16+)【推荐】
// ✅ instanceof 模式匹配(Java 16)
// ❌ 传统写法:强转 + 声明变量
if (obj instanceof String) {
String s = (String) obj;
System.out.println(s.length());
}
// ✅ 一行搞定
if (obj instanceof String s) {
System.out.println(s.length());
}
// ✅ switch 模式匹配(Java 17 preview → 21 正式)
Object obj = // ...
String result = switch (obj) {
case Integer i -> "整数: " + i;
case String s -> "字符串长度: " + s.length();
case Long l -> "长整型: " + l;
case null -> "null 值";
default -> "未知类型";
};
// ✅ 带守卫(when)的模式匹配(Java 17 preview+)
switch (obj) {
case String s when s.length() > 10 -> handleLong(s);
case String s -> handleShort(s);
default -> handleOther(obj);
}
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
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
# 07.跨语言对比
# 7.1 Java vs Kotlin
| 规范项 | Java | Kotlin |
|---|---|---|
| 命名风格 | 小驼峰/大驼峰 | 相同 |
| 空安全 | 需要手动判空 | ? 语法糖 |
| 数据类 | 手写 getter/setter | data class |
| 单例 | 双重检查锁/枚举 | object 关键字 |
| 字符串模板 | String.format | "Hello, $name" |
| 异常 | checked + unchecked | 仅 unchecked |
// Kotlin 风格
data class User(
val name: String,
val age: Int,
val email: String? = null // 可空类型
)
// Java 风格(等效)
public class User {
private final String name;
private final int age;
private final String email; // 需手动处理null
// + getter, equals, hashCode, toString...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 7.2 Java vs Go
| 规范项 | Java | Go |
|---|---|---|
| 命名风格 | camelCase | camelCase(导出用PascalCase) |
| 大括号 | K&R风格 | 强制K&R |
| 异常处理 | try-catch | error返回值 |
| 接口 | 显式实现 | 隐式实现(鸭子类型) |
| 泛型 | 类型擦除 | 1.18+具化泛型 |
| 格式化 | 自行配置 | gofmt 统一 |
// Go 风格:错误处理
func GetUser(id int64) (*User, error) {
user, err := repo.FindById(id)
if err != nil {
return nil, fmt.Errorf("查询用户失败: %w", err)
}
return user, nil
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 7.3 通用编码原则
无论使用哪种语言,以下原则都是通用的:
- DRY(Don't Repeat Yourself):不要重复自己
- KISS(Keep It Simple, Stupid):保持简单
- YAGNI(You Ain't Gonna Need It):不要过度设计
- Fail Fast:尽早暴露错误
- Defensive Programming:防御性编程
┌─────────────┬────────────────────────────────────────────┐
│ 原则 │ 核心要义 │
├─────────────┼────────────────────────────────────────────┤
│ 命名 │ 名字即文档,见名知意 │
│ 格式 │ 统一风格,工具强制执行 │
│ 注释 │ 解释Why,不解释What │
│ 异常 │ 不吞异常,不滥捕获 │
│ 空安全 │ 防御在前,Optional优先 │
│ 并发 │ 明确线程模型,避免共享可变状态 │
│ 设计 │ 高内聚低耦合,优先组合 │
└─────────────┴────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 08.日志规范(阿里规约)
# 8.1 日志框架
应用中不可直接使用日志系统(Log4j、Logback)中的API,应依赖SLF4J门面:
// ✅ SLF4J 门面
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger log = LoggerFactory.getLogger(MyClass.class);
// ❌ 直接使用 Log4j
import org.apache.log4j.Logger;
1
2
3
4
5
6
7
2
3
4
5
6
7
# 8.2 日志级别规范
| 级别 | 场景 | 说明 |
|---|---|---|
| ERROR | 影响到程序正常运行、需要人工介入 | 第三方服务不可用、数据库连接失败 |
| WARN | 可恢复的异常、潜在风险 | 配置缺失使用默认值、重试成功 |
| INFO | 关键流程节点、外部调用 | 服务启动、重要业务操作、RPC调用 |
| DEBUG | 调试信息 | 方法入参出参、中间变量 |
// ✅ 正确使用
log.error("调用支付服务失败, orderId={}", orderId, e);
log.warn("配置项未设置, 使用默认值 timeout={}ms", DEFAULT_TIMEOUT);
log.info("订单创建成功, orderId={}, userId={}, amount={}", orderId, userId, amount);
log.debug("查询条件: {}", query);
// ❌ 错误使用
log.info("处理完成"); // 无关键信息,无法排查问题
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 8.3 日志打印规范
// ✅ 使用占位符,避免字符串拼接
log.info("用户登录, userId={}, ip={}", userId, ip);
// ❌ 字符串拼接(即使级别不匹配也会执行拼接)
log.debug("用户数据: " + user.toString());
// ✅ 条件日志(仅在需要时计算)
if (log.isDebugEnabled()) {
log.debug("详细数据: {}", expensiveComputation());
}
// ✅ 异常日志必须包含堆栈
log.error("处理订单失败, orderId={}", orderId, e); // e 作为最后参数
// ❌ 只打印 message,丢失堆栈
log.error("处理订单失败: " + e.getMessage());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 09.控制语句规范(阿里规约)
# 9.1 switch 语句
// ✅ 每个 case 必须有 break/return,或用 switch 表达式(Java 14+)
String typeName = switch (type) {
case PAYMENT -> "支付订单";
case REFUND -> "退款订单";
default -> "未知类型";
};
// 传统写法必须写 break 并注释 fall-through
switch (type) {
case PAYMENT:
// fall through // 明确注释
case REFUND:
process();
break;
default:
break;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 9.2 条件判断
// ✅ 避免 if-else 层级过深(不超过3层)
// 使用卫语句提前返回
public void process(Order order) {
if (order == null) {
log.warn("订单为空");
return;
}
if (!order.isValid()) {
log.warn("无效订单: {}", order.getId());
return;
}
if (order.isProcessed()) {
return;
}
// 主逻辑
doProcess(order);
}
// ✅ 复杂条件提取为方法
if (isEligibleForDiscount(user, order)) {
applyDiscount(order);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 9.3 循环规范
// ✅ 使用 for-each 遍历
for (String item : list) {
process(item);
}
// ✅ 不要在循环中执行耗时操作(数据库查询、RPC调用)
// 批量查询替代循环查询
List<Long> ids = orders.stream().map(Order::getUserId).collect(toList());
Map<Long, User> userMap = userService.batchGet(ids); // 一次查询
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 10.日期与时间(阿里规约)
// ✅ Java 8+ 使用 java.time
LocalDate today = LocalDate.now();
LocalDateTime now = LocalDateTime.now();
ZonedDateTime zonedNow = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
// ✅ 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String dateStr = now.format(formatter);
// ✅ 计算
LocalDate nextWeek = today.plusWeeks(1);
long days = ChronoUnit.DAYS.between(start, end);
// ❌ 禁止使用 System.currentTimeMillis() 直接做日期计算
// ❌ SimpleDateFormat 线程不安全,禁止作为静态变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 11.equals 与 hashCode(阿里规约)
// ✅ 重写 equals 必须重写 hashCode
// ✅ equals 自反性、对称性、传递性、一致性
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User user)) return false; // Java 16+ pattern matching
return Objects.equals(id, user.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
// ✅ 使用 Objects.equals 避免 NPE
if (Objects.equals(a, b)) { ... } // 而非 a.equals(b)
// ❌ equals 参数不能用除 Object 外的类型(否则是重载不是重写)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 12.序列化规范
// ✅ 显式指定 serialVersionUID
public class UserDTO implements Serializable {
private static final long serialVersionUID = 1L;
}
// ✅ Jackson 注解(推荐,不依赖 Serializable)
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class UserDTO {
private Long id;
@JsonProperty("user_name") // JSON字段名不同于Java字段名
private String userName;
@JsonIgnore // 不序列化
private String password;
@JsonInclude(Include.NON_NULL) // null 不序列化
private String email;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 13.枚举使用(阿里规约)
// ✅ 枚举命名全大写,也可以用 Enum 替代常量类
public enum OrderStatus {
PENDING("待支付"),
PAID("已支付"),
SHIPPED("已发货"),
CANCELLED("已取消");
private final String desc;
OrderStatus(String desc) { this.desc = desc; }
}
// ✅ 枚举方法:每个常量可以有自己的行为
public enum Operation {
PLUS { double apply(double a, double b) { return a + b; } },
MINUS { double apply(double a, double b) { return a - b; } };
abstract double apply(double a, double b);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 14.数组与泛型补充
// ✅ 使用 List 优于数组(泛型兼容性更好)
// ❌ 数组协变,运行时可能 ClassCastException
Object[] arr = new String[1];
arr[0] = 1; // ArrayStoreException 运行时
// ✅ 使用泛型集合
List<String> list = new ArrayList<>();
// ✅ 数组转 List 注意事项
// ❌ Arrays.asList() 返回固定大小,不可 add/remove
List<String> fixed = Arrays.asList("a", "b");
// ✅ 可变 List
List<String> mutable = new ArrayList<>(Arrays.asList("a", "b"));
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 15.MySQL 数据库规范(阿里规约)
-- ✅ 表名/字段名:小写 + 下划线
CREATE TABLE user_order (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
user_id BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
order_no VARCHAR(64) NOT NULL COMMENT '订单号',
amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '金额',
status TINYINT NOT NULL DEFAULT 0 COMMENT '状态',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
INDEX idx_user_id (user_id),
UNIQUE INDEX uk_order_no (order_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户订单表';
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
// ✅ 不使用 SELECT *
// ✅ 必须使用参数化查询,防止 SQL 注入
String sql = "SELECT id, name, email FROM user WHERE id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setLong(1, userId);
// ❌ 直接拼接 SQL
String sql = "SELECT * FROM user WHERE name = '" + name + "'"; // SQL注入风险
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 16.单元测试规范
// ✅ JUnit 5 + Mockito
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private OrderRepository repository;
@InjectMocks
private OrderService service;
@Test
@DisplayName("创建订单成功")
void shouldCreateOrderSuccessfully() {
// Given
CreateOrderRequest request = new CreateOrderRequest(1L, "item", 100);
when(repository.save(any())).thenReturn(mockOrder);
// When
Order result = service.create(request);
// Then
assertNotNull(result);
assertEquals(OrderStatus.PENDING, result.getStatus());
verify(repository).save(any());
}
// ✅ 测试异常场景
@Test
void shouldThrowWhenUserNotFound() {
when(repository.findById(1L)).thenReturn(Optional.empty());
assertThrows(UserNotFoundException.class, () -> service.getUser(1L));
}
}
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
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
测试覆盖率:
- 核心业务方法 100%
- 分支覆盖(每个 if 的两条路径都要测)
- 异常路径必须覆盖
# 17.安全规约(阿里规约)
// ✅ 敏感数据脱敏
public class UserVO {
private String name;
@JsonSerialize(using = PhoneDesensitize.class)
private String phone; // 138****1234
@JsonIgnore // 密码绝不返回前端
private String password;
}
// ✅ 参数校验
@PostMapping("/users")
public Result createUser(@Valid @RequestBody CreateUserRequest request) {
// @Valid 触发 javax.validation 校验
}
public class CreateUserRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20)
private String name;
@Email
private String email;
@Pattern(regexp = "^1[3-9]\\d{9}$")
private String phone;
}
// ✅ 权限控制
@PreAuthorize("hasRole('ADMIN')") // Spring Security
public Result deleteUser(Long id) { ... }
// ❌ 禁止在代码中硬编码密钥/密码
private static final String SECRET = "my-secret-key"; // 危险!
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
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
# 18.工程结构规范
com.company.project
├── controller/ # HTTP接口层,薄薄一层只做参数转换
├── service/ # 业务逻辑层
│ └── impl/
├── repository/ # 数据访问层(DAO)
├── model/
│ ├── entity/ # 数据库实体
│ ├── dto/ # 数据传输对象
│ └── vo/ # 视图对象
├── config/ # 配置类
├── common/
│ ├── exception/ # 自定义异常
│ ├── constant/ # 常量
│ └── util/ # 工具类
└── Application.java
# 分层调用:Controller → Service → Repository
# 禁止反向依赖、同层循环依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 19.Stream API 规范
# 19.1 基本使用原则
// ✅ 优先使用方法引用
List<String> names = users.stream()
.map(User::getName)
.collect(Collectors.toList());
// ❌ 冗余的 lambda
List<String> names = users.stream()
.map(u -> u.getName())
.collect(Collectors.toList());
// ✅ 复杂操作应提取为方法
String result = users.stream()
.filter(this::isActive)
.map(this::formatUser)
.collect(Collectors.joining(","));
// ❌ Stream 中副作用过多(日志、状态修改混在 Stream 里)
users.stream()
.peek(u -> log.info("处理用户 {}", u.getId())) // 仅调试用
.filter(User::isActive)
.forEach(u -> u.setStatus(1)); // ❌ forEach 中修改状态不清真
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 19.2 常见误用与改进
// ❌ Stream 中抛检查异常很丑
List<String> result = paths.stream()
.map(path -> {
try {
return Files.readString(Path.of(path));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
})
.collect(toList());
// ✅ 把异常处理提取为方法
private String readFileSafe(String path) {
try { return Files.readString(Path.of(path)); }
catch (IOException e) { throw new UncheckedIOException(e); }
}
List<String> result = paths.stream().map(this::readFileSafe).collect(toList());
// ❌ 用 forEach 代替 for 循环(没有简洁性提升)
users.stream().forEach(u -> process(u)); // Stream 创建有开销
// ✅ 直接用 for-each(简单场景)
for (User u : users) {
process(u);
}
// Stream 适用场景:filter + map + collect 链条 > 2 步
// for-each 适用场景:单步遍历 + 简单操作
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
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
# 19.3 性能注意事项
// ❌ 每次调用都创建新 Stream(热路径中避免)
for (int i = 0; i < 1000; i++) {
long count = users.stream().filter(User::isActive).count(); // 创建1000个Stream对象
}
// ✅ 提前计算,复用结果
long activeCount = users.stream().filter(User::isActive).count();
for (int i = 0; i < 1000; i++) {
doSomething(activeCount);
}
// ✅ 基本类型用特化 Stream(IntStream、LongStream,避免装箱)
IntStream.range(0, 100).sum(); // ✅ 无装箱
Stream.of(1, 2, 3).mapToInt(i -> i); // ✅ 显式转换
Stream.of(1, 2, 3).reduce(0, Integer::sum); // ❌ 每次 reduce 装箱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 20.Lombok 使用规范
# 20.1 推荐使用的注解
// ✅ @Data —— 用在纯数据类(DTO/VO/Entity)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {
private Long id;
private String name;
private Integer age;
}
// ✅ @Builder —— 构建复杂对象(推荐用于多参数构造)
@Builder
public class OrderRequest {
private Long userId;
private String productCode;
private BigDecimal amount;
private String couponCode; // 可选
private String remark; // 可选
}
// 调用方:意图清晰,参数可选
OrderRequest req = OrderRequest.builder()
.userId(1L)
.productCode("SKU001")
.amount(new BigDecimal("99.00"))
.remark("加急配送")
.build();
// ✅ @Slf4j —— 日志声明
@Slf4j
public class OrderService {
public void process(Order order) {
log.info("处理订单: {}", order.getId());
}
}
// ✅ @RequiredArgsConstructor —— 注入 final 字段(Spring 推荐)
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepo; // 构造器注入
private final PaymentGateway paymentGateway;
}
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
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
# 20.2 谨慎使用的注解
// ⚠️ @EqualsAndHashCode —— 继承体系中有坑
// 仅用于无继承关系的简单 POJO
// ⚠️ @ToString —— 注意循环引用
@Data
@ToString(exclude = "parent") // 排除双向关联字段
public class Category {
private Long id;
private String name;
private Category parent; // 排除,避免递归爆栈
private List<Category> children;
}
// ⚠️ @Getter/@Setter —— 如非必要,不开放不必要的 setter
public class Order {
@Getter private Long id;
@Getter private BigDecimal amount;
@Getter private OrderStatus status;
// 不生成 setter —— 通过业务方法修改状态
public void pay() { this.status = OrderStatus.PAID; }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 20.3 禁止使用的注解
// ❌ @SneakyThrows —— 吞掉编译期异常检查,破坏异常体系
// @SneakyThrows
// public void readFile() { Files.readString(path); }
// ❌ @Cleanup —— 不如 try-with-resources 清晰
// try (var reader = new BufferedReader(...)) { } ← 更标准,一眼看出资源释放
// ❌ @NonNull —— 不如 @lombok.NonNull 明确,更推荐 javax.validation 或 Objects.requireNonNull
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 21.性能优化规范
# 21.1 字符串与集合
// ✅ 循环拼接:必须用 StringBuilder
StringBuilder sb = new StringBuilder(expectedSize);
for (String item : list) {
sb.append(item).append(delimiter);
}
String result = sb.toString();
// ✅ 指定初始容量,减少扩容
List<String> list = new ArrayList<>(1000); // 已知大致大小
Map<String, User> map = new HashMap<>(256); // 2的幂
Set<String> set = new HashSet<>(expectedSize);
// ✅ String.intern() 谨慎使用 —— 仅用于大量重复字符串
// 太多 intern 会撑爆 StringTable(默认 60013 个)
// ✅ 避免在循环中使用正则
// ❌ 每次循环都编译 Pattern
for (String s : list) {
s.matches("\\d{4}-\\d{2}-\\d{2}");
}
// ✅ 提前编译
private static final Pattern DATE_PATTERN = Pattern.compile("\\d{4}-\\d{2}-\\d{2}");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 21.2 对象创建与 GC
// ✅ 局部变量优先用基本类型(减少堆分配)
int count = 0; // ✅ 栈上,轻量
Integer count = 0; // ❌ 堆分配,自动装箱
// ✅ 对象池:常用小对象可复用(Integer缓存 -128~127)
Integer a = 127, b = 127;
System.out.println(a == b); // true(缓存池命中)
// ✅ 懒加载:不立即创建昂贵对象
private volatile ExpensiveObject cache; // volatile 可见性
// ✅ try-with-resources 确保流关闭
try (var is = new FileInputStream(file);
var reader = new BufferedReader(new InputStreamReader(is))) {
return reader.lines().collect(toList());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 21.3 数据库与 IO
// ✅ 批量操作:一条SQL替代N条
// ❌ N次查询
for (Long id : ids) {
orderRepo.findById(id); // 100次往返!
}
// ✅ 1次查询
List<Order> orders = orderRepo.findByIdIn(ids);
// ✅ 分页查询:必加 limit
// ❌ SELECT * FROM orders WHERE user_id = ?
// ✅ SELECT * FROM orders WHERE user_id = ? LIMIT 100 OFFSET ?
// ✅ 连接池复用:不要每次 new Connection
// Spring Boot 默认 HikariCP,配置合理即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 21.4 缓存策略 【推荐】
// ✅ 本地缓存:Caffeine(推荐,比 Guava Cache 更优)
private final Cache<Long, User> userCache = Caffeine.newBuilder()
.maximumSize(10_000) // 最大条目
.expireAfterWrite(30, TimeUnit.MINUTES) // 写入后过期
.recordStats() // 开启统计
.build();
User user = userCache.get(userId, key -> userRepo.findById(key));
// ✅ 多级缓存:本地 + Redis
public User getUserWithCache(Long id) {
// L1:本地缓存
User user = localCache.getIfPresent(id);
if (user != null) return user;
// L2:Redis 缓存
String json = redisTemplate.opsForValue().get("user:" + id);
if (json != null) {
user = objectMapper.readValue(json, User.class);
localCache.put(id, user);
return user;
}
// L3:数据库
user = userRepo.findById(id).orElse(null);
if (user != null) {
redisTemplate.opsForValue().set("user:" + id, toJson(user), 1, TimeUnit.HOURS);
localCache.put(id, user);
}
return user;
}
// ✅ 缓存穿透防御:空值缓存
User user = userCache.get(id, key -> {
User u = userRepo.findById(key).orElse(null);
return u != null ? u : User.EMPTY; // 缓存空对象,防穿透
});
// ❌ 无过期/无上限的缓存——OOM 隐患
Map<Long, User> cache = new HashMap<>(); // 越放越多
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
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
# 22.工具链与自动化规范
# 22.1 静态代码检查
| 工具 | 用途 | 集成方式 |
|---|---|---|
| Checkstyle | 代码风格检查(命名、空格、大括号) | Maven/Gradle 插件,CI 中执行 |
| SpotBugs | 字节码级别的 Bug 检测 | Maven/Gradle 插件 |
| SonarQube | 代码质量综合扫描(Bug/漏洞/坏味道) | CI 流水线 |
| PMD | 代码坏味道检测(重复代码、过长方法) | Maven 插件 |
<!-- pom.xml Checkstyle 配置 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<configLocation>checkstyle.xml</configLocation>
<failOnViolation>true</failOnViolation> <!-- CI 中设为 true -->
</configuration>
<executions>
<execution>
<phase>validate</phase>
<goals><goal>check</goal></goals>
</execution>
</executions>
</plugin>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 22.2 代码格式化
<!-- .editorconfig:跨编辑器统一格式 -->
[*.java]
indent_style = space
indent_size = 4
max_line_length = 120
1
2
3
4
5
2
3
4
5
# Spotless:一键格式化(Google Java Format 风格)
./mvnw spotless:apply
1
2
2
# 22.3 CI 集成
# GitHub Actions 示例
- name: Code Quality Check
run: |
./mvnw checkstyle:check # 风格检查
./mvnw spotbugs:check # Bug 检测
./mvnw test # 单元测试
1
2
3
4
5
6
2
3
4
5
6
# 22.4 架构测试 【推荐】
用 ArchUnit 在单元测试里强制架构约束:
@Test
void controllers_should_not_depend_on_repositories_directly() {
classes().that().resideInAPackage("..controller..")
.should().onlyDependOnClassesThat()
.resideInAnyPackage("..service..", "..dto..", "java..")
.check(classes);
}
@Test
void service_impls_should_be_annotated_with_Service() {
classes().that().haveSimpleNameEndingWith("ServiceImpl")
.should().beAnnotatedWith(Service.class)
.check(classes);
}
@Test
void no_class_should_depend_on_implementation_package() {
noClasses().that().resideOutsideOfPackage("..impl..")
.should().dependOnClassesThat()
.resideInAPackage("..impl..")
.check(classes);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
其他推荐工具:
| 工具 | 用途 |
|---|---|
| Spotless | 代码格式化(支持 Google/Palantir/AOSP 风格) |
| jqwik | 基于属性的测试(Property-Based Testing) |
| OWASP Dependency-Check | 依赖漏洞扫描 |
| JaCoCo | 代码覆盖率报告(集成 SonarQube) |
# 23.代码审查清单
每次 Code Review 时,按以下清单逐项检查:
## 命名
- [ ] 类/方法/变量名称是否见名知意
- [ ] 不存在拼音、单字母、数字编号的变量名
- [ ] 布尔变量使用 is/has/can 前缀
## 格式
- [ ] 缩进统一(4空格),无 Tab 混杂
- [ ] 大括号 K&R 风格
- [ ] 单行不超过120字符
## 注释
- [ ] 注释解释"为什么"而非"做了什么"
- [ ] 公共 API 有完整的 Javadoc
- [ ] 无明显无意义注释(`// i++ 自增`)
## 逻辑
- [ ] if 嵌套不超过 3 层
- [ ] 方法体不超过 80 行
- [ ] 无重复代码块(DRY)
- [ ] 异常正确捕获和处理,无空 catch 块
## 健壮性
- [ ] 入参有 null 校验
- [ ] 集合返回空集合而非 null
- [ ] 数据库查询有 LIMIT
- [ ] 敏感信息无硬编码
- [ ] 可变对象做了防御性拷贝(Date、集合等)
## 现代 Java
- [ ] 纯数据类优先考虑 Record(Java 14+)
- [ ] 有限子类层次用 Sealed Class(Java 17+)
- [ ] instanceof 用了模式匹配简化(Java 16+)
- [ ] 复杂 switch 用 switch 表达式(Java 14+)
- [ ] Stream 操作链 > 2 步才用 Stream
## 性能
- [ ] 循环中无数据库/RPC 调用
- [ ] 循环中无 `new SimpleDateFormat()` 或 `Pattern.compile()`
- [ ] 集合初始化指定了合理容量
- [ ] 缓存有大小上限和过期时间
## 测试
- [ ] 核心逻辑有单元测试
- [ ] 正向和异常路径都覆盖
- [ ] 测试方法命名清晰表达了测试意图
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
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
# 24.常见陷阱速查
# 24.1 空指针陷阱
| # | 陷阱 | 正解 |
|---|---|---|
| 1 | user.getName().equals("yc") → user.getName() 为 null | "yc".equals(user.getName()) |
| 2 | getOrders() 返回 null | 返回 Collections.emptyList() |
| 3 | Map.get() 返回 null 直接拆箱 | Long v = map.get(key); if (v != null) ... |
| 4 | Optional.get() 不检查 | optional.orElseThrow() / optional.ifPresent() |
| 5 | Arrays.asList() 的元素修改影响数组 | 用 new ArrayList<>(Arrays.asList(...)) |
# 24.2 集合陷阱
| # | 陷阱 | 正解 |
|---|---|---|
| 1 | Arrays.asList() 不可 add/remove | new ArrayList<>(...)包装 |
| 2 | for-each 中删除元素 → ConcurrentModificationException | list.removeIf(...) 或 Iterator.remove() |
| 3 | list.contains(o) 依赖 equals,未重写则比较引用 | 重写 equals/hashCode |
| 4 | HashMap key 可变对象 → hash 变了找不到 | key 用不可变对象(String、Integer、LocalDate) |
| 5 | Set 用自定义对象没重写 hashCode → 重复插入 | 重写 equals/hashCode |
# 24.3 并发陷阱
| # | 陷阱 | 正解 |
|---|---|---|
| 1 | HashMap 多线程 → 死循环(JDK7)/ 数据错乱 | ConcurrentHashMap |
| 2 | SimpleDateFormat 多线程 → 数据错乱 | DateTimeFormatter(线程安全) |
| 3 | ++count 不是原子操作 | AtomicInteger.getAndIncrement() |
| 4 | double-checked locking 没用 volatile | volatile + synchronized,或直接用枚举单例 |
| 5 | Thread.stop() / suspend() | 用 interrupt() + 协作式停止 |
# 24.4 性能陷阱
| # | 陷阱 | 正解 |
|---|---|---|
| 1 | 循环中 String += | StringBuilder |
| 2 | 循环中 Pattern.compile() / new SimpleDateFormat() | 提取为 static final 常量 |
| 3 | Exception.printStackTrace() 吞异常 | logger.error(...) 记录完整堆栈 |
| 4 | 大对象 ThreadLocal 忘记 remove | try { ... } finally { threadLocal.remove(); } |
| 5 | 装箱类型循环中频繁用 == | equals() 比较(或 -XX:+UnlockExperimentalVMOptions) |
| 6 | Stream 中抛检查异常 → try-catch 嵌套 | 提取为独立方法 |
上次更新: 2026/06/17, 09:06:19