代码规范约定
本教程有非常严格的代码规范。这份规范有两个目的:
- 让你看得舒服 — 全文统一风格,降低阅读负担
- 让你直接用 — 这些规范都是从阿里 Java 开发手册 + Google Java Style + 各大厂实践综合而来,企业项目直接照搬
命名规范
| 类型 | 规则 | 示例 |
|---|---|---|
| 类名 | UpperCamelCase(首字母大写) | UserController、OrderService |
| 方法名 | lowerCamelCase(首字母小写) | getUserById、calculateTotal |
| 变量名 | lowerCamelCase | userName、orderList |
| 常量名 | UPPER_SNAKE_CASE | MAX_RETRY_COUNT、DEFAULT_TIMEOUT |
| 包名 | 全小写,名词 | com.example.user、com.taskflow.rbac |
| 接口名 | 名词或形容词(不加 I 前缀) | UserService、Comparable |
| 枚举名 | UpperCamelCase | OrderStatus |
| 枚举值 | UPPER_SNAKE_CASE | PENDING、PAID、SHIPPED |
❌ 反例(新手常见错误)
java
// ❌ 全用拼音首字母
public class Yhqx { // 应为 UserController
public void chaxun() { } // 应为 query
public String xm; // 应为 userName
}
// ❌ 缩写不一致
public String getUsrName(); // 一会儿 User 一会儿 Usr
public String getUserID(); // ID 应为 Id
// ❌ 复数形式混乱
private List<User> userList; // 下个方法用 users
private List<User> users; // 应统一为 userList 或 userItems✅ 正例(企业级标准)
java
/**
* 用户控制器
*
* @author taskflow
* @since 1.0.0
*/
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
public class UserController { // ① 类名 UpperCamelCase
private final UserService userService; // ② 依赖注入用 final + 构造器
@GetMapping("/{id}")
public Result<UserVO> getUserById( // ③ 方法名 lowerCamelCase
@PathVariable @Min(1) Long id // ④ 参数用包装类,便于校验 null
) {
UserDO user = userService.getById(id);
return Result.success(UserConverter.toVO(user));
}
private static final int MAX_PAGE_SIZE = 100; // ⑤ 常量 UPPER_SNAKE_CASE
}注释规范
本教程代码注释有三种层次:
1. 行内 // 数字 注释:解释设计意图
java
public class OrderService {
private static final int MAX_RETRY = 3; // ① 重试上限:3 次是支付场景的业界经验值
public void pay(Order order) {
// ② 先扣库存再支付,避免超卖(高一致性需求)
inventoryService.deduct(order);
try {
paymentService.charge(order);
} catch (PaymentException e) {
// ③ 支付失败必须回滚库存,否则用户付不了钱但库存没了
inventoryService.restore(order);
throw e;
}
}
}2. Javadoc:/** */:解释对外契约
java
/**
* 根据用户 ID 查询用户信息
*
* @param id 用户 ID(必须 > 0)
* @return 用户视图对象;用户不存在时返回 null
* @throws IllegalArgumentException 当 id <= 0 时抛出
* @since 1.0.0
*/
public UserVO getUserById(Long id);3. 「为什么这样写」卡片:解释架构决策
markdown
::: tip 为什么用构造器注入而不是 @Autowired 字段注入?
字段注入的三个问题:
1. 无法注入 final 字段,变量可被外部修改
2. 无法在没有 Spring 容器的单元测试中手动构造对象
3. 隐藏了类的依赖关系,不看代码无法知道这个类需要什么
构造器注入的三个好处:
1. final 字段保证不可变,线程安全
2. 测试时可以 `new UserService(mockUserDao, mockLogger)`
3. 类的依赖在构造器签名上一目了然
这是 Spring 官方推荐的方式(Spring 4.3+ 单构造器可省略 @Autowired)。
:::代码格式规范
缩进与空格
java
// ✅ 4 空格缩进(不用 Tab)
public void method() {
if (condition) {
doSomething();
}
}
// ✅ 操作符前后加空格
int sum = a + b * c;
// ✅ 关键字后加空格
if (x > 0) { }
for (int i = 0; i < 10; i++) { }大括号
java
// ✅ K&R 风格:左大括号不换行
public void method() {
if (condition) {
// ...
} else { // else 与右大括号同行
// ...
}
}
// ❌ Allman 风格(左大括号换行)不推荐
public void method()
{
if (condition)
{
// ...
}
}行长度
java
// ✅ 单行不超过 120 字符(IDE 自动换行)
// ✅ 字符串拼接用 + 或 String.join,复杂场景用 StringBuilder
String message = String.format(
"用户 [%s] 在 [%s] 登录失败,错误码 [%d]",
username,
LocalDateTime.now(),
errorCode
);空行
java
// ✅ 方法之间空一行
public void method1() { }
// ✅ 类成员之间空一行(字段、构造器、方法分组)
public class UserService {
private static final Logger log = LoggerFactory.getLogger(UserService.class);
private final UserDao userDao;
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public User getById(Long id) {
return userDao.selectById(id);
}
}异常处理规范
✅ 使用具体异常,不用通用 Exception
java
try {
userService.create(user);
} catch (BusinessException e) { // ✅ 捕获业务异常
return Result.fail(e.getCode(), e.getMessage());
} catch (DataAccessException e) { // ✅ 捕获数据访问异常
log.error("创建用户失败", e);
return Result.fail("DB_ERROR", "系统繁忙");
}❌ 反例
java
try {
userService.create(user);
} catch (Exception e) { // ❌ 太宽泛,可能掩盖严重错误
e.printStackTrace(); // ❌ 不能用 printStackTrace,应用日志框架
}注释 vs 日志
java
// ❌ 反例:注释里写操作步骤
public void save() {
// 1. 校验参数
validate();
// 2. 保存到数据库
dao.save();
// 3. 发送通知
notify();
}
// ✅ 正例:注释解释"为什么",方法名解释"做了什么"
public void save() {
validate(); // 校验放最前,失败快速返回(Fail-Fast)
dao.save();
notify(); // 通知失败不影响主流程(最终一致性)
}import 顺序
java
// 1. JDK 自带
import java.util.List;
import java.time.LocalDateTime;
// 2. 第三方框架
import org.springframework.web.bind.annotation.RestController;
import com.baomidou.mybatisplus.annotation.TableName;
// 3. 本项目内部
import com.taskflow.rbac.entity.User;
import com.taskflow.rbac.service.UserService;
// 4. 静态导入放最后(如果使用)
import static java.lang.Math.PI;IDEA 默认可以自动排序 import,建议开启 "Optimize imports on the fly"。
命名一致性原则
一个项目里,同一个概念用同一个名字:
java
// ❌ 一个文件里 user、usr、userInfo 混用
User user = getUser();
User usr = getUsrById(id);
UserInfo info = getUserInfoByName(name);
// ✅ 全项目统一用 user
User user = getUser();
User userById = getUserById(id);
User userByName = getUserByName(name);一行不超过一个语句
java
// ❌ 反例
int a = 1; int b = 2;
// ✅ 正例
int a = 1;
int b = 2;不要省略大括号
java
// ❌ 反例(即使一行也不要省)
if (condition) doSomething();
// ✅ 正例
if (condition) {
doSomething();
}原因:未来加一行时容易漏加大括号,导致 bug。
方法长度
- ✅ 单个方法不超过 80 行(屏幕一屏能看完)
- ✅ 超过就拆分,提取私有方法
- ✅ 拆分的原则:一个方法只做一件事
java
// ❌ 反例:一个方法 200 行
public void processOrder(Order order) {
// 校验参数...
// 计算价格...
// 扣库存...
// 创建订单...
// 发送通知...
// 记录日志...
}
// ✅ 正例:拆分为多个方法
public void processOrder(Order order) {
validateOrder(order);
BigDecimal price = calculatePrice(order);
deductInventory(order, price);
createOrder(order, price);
notifyAfterOrderCreated(order);
}工具配置建议
IDEA 必备插件
| 插件 | 作用 |
|---|---|
| Alibaba Java Coding Guidelines | 阿里规约实时检查 |
| SonarLint | 代码质量检查 |
| Lombok Helper | 简化样板代码 |
| MyBatisX | MyBatis 跳转 |
| GitToolBox | Git 状态栏 |
| Rainbow Brackets | 彩虹括号(嵌套多了很有用) |
| Translation | 翻译选中文字 |
IDEA 关键配置
File → Settings → Editor → Code Style → Java:
- Tab/Indent: Use tab character = OFF, Indent = 4, Continuation indent = 8
- Wrapping and Braces: Braces placement = Same line
- Imports: Class count to use import with '*' = 99
- Import order: 按上面 4 组顺序配置
File → Settings → Editor → File Encodings:
- Global Encoding: UTF-8
- Project Encoding: UTF-8
- Default encoding for properties: UTF-8以上就是本教程的代码规范。后续所有章节的代码都会遵循这些约定。
如果你已经掌握这些规范,可以开始 第 1 章:为什么学 Java 了。