[代码规范篇]Java代码规范
[代码规范篇]Java代码规范
本文旨在介绍Java代码在编写、设计时的一些注意点,帮助后端新人养成一个好习惯,同时规范开发,需要注意的是本文内容不全,以及不同公司一些规范可能有差别,大家以自己司内风格为准即可。
想要看Go规范的同学,可以参考这篇文档:
[代码规范篇]Go代码规范
- 不同公司之间,编码规范都有不同,大家自行灵活变通即可,但大体方向相差无几。
- 大家也可以去看阿里巴巴出的Java编码规范,里面有更细致的讲解:《阿里巴巴Java开发手册(终极版)》免费在线阅读_藏经阁
1. 资源管理
原则:
- 及时释放资源:使用 try-with-resources 或 finally 块确保资源被正确关闭
- 避免资源泄漏:数据库连接、文件句柄、网络连接等必须显式关闭
- 空值检查:访问对象前必须进行空值检查
// 不推荐:未关闭资源
public String readFile(String filename) throws IOException {FileInputStream fis = new FileInputStream(filename);byte[] data = fis.readAllBytes();return new String(data);
}// 推荐:使用 try-with-resources 自动关闭资源
public String readFile(String filename) throws IOException {try (FileInputStream fis = new FileInputStream(filename)) {byte[] data = fis.readAllBytes();return new String(data);}
}// 不推荐:未检查空值
public void processUser(User user) {System.out.println(user.getName()); // 可能抛出 NullPointerException
}// 推荐:检查空值
public void processUser(User user) {if (user == null) {return;}System.out.println(user.getName());
}
2. 并发安全
原则:
- 明确数据所有权:清晰界定哪些线程可以访问和修改哪些数据
- 锁范围最小化:减少锁的持有时间,只锁住临界区
- 优先使用并发工具类:使用 java.util.concurrent 包中的工具类
- 避免死锁:统一加锁顺序,避免循环等待
// 不推荐:在锁中执行耗时操作
public void badExample(Lock lock, Map<String, Integer> data) throws InterruptedException {lock.lock();try {// 模拟耗时操作Thread.sleep(1000);data.put("key", 1);} finally {lock.unlock();}
}// 推荐:尽快释放锁
public void goodExample(Lock lock, Map<String, Integer> data) throws InterruptedException {lock.lock();try {data.put("key", 1);} finally {lock.unlock();}// 耗时操作在锁外执行Thread.sleep(1000);
}
3. 敏感信息安全
原则:
- 最小权限原则:只赋予程序运行所需的最小权限
- 输入校验:对所有外部输入进行严格的长度、类型、格式校验
- 敏感信息保护:避免硬编码密钥、密码等敏感信息
- 防止SQL注入:使用预编译语句
// 不推荐:硬编码敏感信息
private static final String DB_PASSWORD = "root123";// 推荐:通过环境变量或配置文件读取
public String getDBPassword() {return System.getenv("DB_PASSWORD");
}// 不推荐:容易造成SQL注入
public User getUserById(String id) throws SQLException {String sql = "SELECT * FROM users WHERE id = " + id;Statement stmt = connection.createStatement();ResultSet rs = stmt.executeQuery(sql);// ...return null;
}// 推荐:使用PreparedStatement防止SQL注入
public User getUserById(String id) throws SQLException {String sql = "SELECT * FROM users WHERE id = ?";try (PreparedStatement pstmt = connection.prepareStatement(sql)) {pstmt.setString(1, id);try (ResultSet rs = pstmt.executeQuery()) {// 处理结果集return null;}}
}
4. 日志
原则:
- 恰当使用日志级别:根据信息重要性选择合适的日志级别
- 避免敏感信息:日志中不记录密码、密钥等敏感信息
- 包含上下文信息:日志应包含足够的上下文信息便于排查问题
- 使用参数化日志:避免字符串拼接提高性能
// 不推荐:日志级别使用不当
logger.info("User login failed"); // 应该使用warn级别。具体级别根据实际业务场景来综合评判// 推荐:恰当使用日志级别
logger.debug("Processing user data for user ID: {}", userId);
logger.info("User {} logged in successfully", userId);
logger.warn("Login failed for user: {}", userId);
logger.error("Database connection failed", exception);// 不推荐:记录敏感信息
logger.info("User password: {}", password);// 推荐:避免记录敏感信息
logger.info("User login attempt for user: {}", username);
5. 异常处理
原则:
- 正确处理异常:不能忽略异常,应处理或传递
- 保留异常链:保留原始异常信息便于问题排查
- 具体异常类型:抛出具体的异常类型而非通用异常
- 避免空catch块:不能捕获异常却不处理
// 不推荐:忽略异常
public String readFile(String filename) {try {return new String(Files.readAllBytes(Paths.get(filename)));} catch (IOException e) {return ""; // 忽略异常}
}// 推荐:处理异常
public String readFile(String filename) throws IOException {try {return new String(Files.readAllBytes(Paths.get(filename)));} catch (IOException e) {logger.error("Failed to read file: {}", filename, e);throw new IOException("Failed to read file: " + filename, e);}
}
6. 测试
原则:
- 单元测试覆盖:关键逻辑和公开方法必须有单元测试
- 测试独立性:每个测试方法应独立,不依赖其他测试
- 依赖注入:通过接口而非具体实现便于Mock测试
- 边界条件测试:测试正常、异常和边界条件
// 定义接口以便mock
public interface UserRepository {User findById(Long id);void save(User user);
}// 使用Mock进行测试
@Test
public void testFindUserById() {// GivenUserRepository mockRepository = mock(UserRepository.class);UserService userService = new UserService(mockRepository);User expectedUser = new User(1L, "John");when(mockRepository.findById(1L)).thenReturn(expectedUser);// WhenUser actualUser = userService.findUserById(1L);// ThenassertEquals(expectedUser, actualUser);verify(mockRepository).findById(1L);
}
7. 性能
原则:
- 避免重复计算:缓存计算结果,避免重复计算
- 合理使用集合:选择合适的数据结构和初始容量
- 字符串处理优化:大量字符串拼接使用 StringBuilder
- 避免内存泄漏:及时清理不用的对象引用
// 不推荐:重复计算
public double calculateTotalWithTax(List<Order> orders) {double total = 0;for (Order order : orders) {// 每次循环都调用getTaxRate(),如果这是个昂贵的操作就浪费了total += order.getAmount() * (1 + getTaxRate());}return total;
}// 推荐:避免重复计算
public double calculateTotalWithTax(List<Order> orders) {// 只计算一次税率double taxRate = getTaxRate();double total = 0;for (Order order : orders) {total += order.getAmount() * (1 + taxRate);}return total;
}
8. 项目设计
原则:
- 分层架构:采用清晰的分层架构(Controller-Service-Repository)
- 单一职责:每个类只负责一个功能领域
- 依赖倒置:依赖抽象而非具体实现
- 开闭原则:对扩展开放,对修改关闭
// Controller层
@RestController
@RequestMapping("/users")
public class UserController {private final UserService userService;public UserController(UserService userService) {this.userService = userService;}@PostMappingpublic ResponseEntity<UserDTO> createUser(@RequestBody CreateUserRequest request) {User user = userService.createUser(request.getName(), request.getEmail());return ResponseEntity.ok(UserDTO.from(user));}
}
9. 接口设计
原则:
- 接口隔离:使用专门的接口,而不是庞大的总接口
- 默认方法:合理使用默认方法提供通用实现
- 明确契约:接口方法应有明确的契约和文档说明
// 不推荐:臃肿的接口
public interface BadUserService {User createUser(String name, String email);User updateUser(Long id, String name, String email);void deleteUser(Long id);List<User> getAllUsers();User findUserById(Long id);List<User> findUsersByName(String name);void sendEmail(Long userId, String content);void sendSms(Long userId, String content);void generateReport();void backupData();// 更多不相关的方法...
}// 推荐:接口隔离
public interface UserManagementService {User createUser(String name, String email);User updateUser(Long id, String name, String email);void deleteUser(Long id);List<User> getAllUsers();User findUserById(Long id);List<User> findUsersByName(String name);
}public interface NotificationService {void sendEmail(Long userId, String content);void sendSms(Long userId, String content);
}public interface ReportingService {void generateReport();
}public interface BackupService {void backupData();
}// 不推荐:接口方法缺乏文档说明
public interface UserService {User findUser(Long id);List<User> searchUsers(String keyword);
}// 推荐:明确的接口契约
/*** 用户服务接口,提供用户管理相关功能*/
public interface UserService {/*** 根据ID查找用户* @param id 用户ID,不能为空且必须大于0* @return 用户对象,如果未找到返回null* @throws IllegalArgumentException 当ID无效时抛出*/User findUser(Long id);/*** 根据关键字搜索用户* @param keyword 搜索关键字,不能为空* @return 匹配的用户列表,不会返回null* @throws IllegalArgumentException 当关键字为空时抛出*/List<User> searchUsers(String keyword);
}
10. 类设计
原则:
- 封装性:合理使用访问修饰符,隐藏内部实现
- 不可变性:对于值对象,尽量设计为不可变类
- 继承与组合:优先使用组合而非继承
- 构造函数设计:提供清晰的构造函数和工厂方法
// 不推荐:封装性差,缺乏不可变性
public class BadUser {public Long id; // 应该是privatepublic String name; // 应该是privatepublic String email; // 应该是privatepublic BadUser(Long id, String name, String email) {this.id = id;this.name = name;this.email = email;}
}// 不推荐:使用继承而非组合
public class BadUserService extends DatabaseConnection {public User findUser(Long id) {return queryUser(id); // 直接使用父类方法}
}// 推荐:良好的封装性
public class GoodUser {private final Long id;private final String name;private final String email;public GoodUser(Long id, String name, String email) {this.id = Objects.requireNonNull(id, "ID cannot be null");this.name = Objects.requireNonNull(name, "Name cannot be null");this.email = Objects.requireNonNull(email, "Email cannot be null");}// 只提供getter,不提供setter保证不可变性public Long getId() { return id; }public String getName() { return name; }public String getEmail() { return email; }
}// 推荐:使用组合而非继承
public class GoodUserService {private final DatabaseConnection databaseConnection; // 组合public GoodUserService(DatabaseConnection databaseConnection) {this.databaseConnection = Objects.requireNonNull(databaseConnection);}public User findUser(Long id) {return databaseConnection.queryUser(id);}
}// 推荐:清晰的构造函数设计
public class Order {private final Long id;private final String orderNumber;private final BigDecimal amount;private OrderStatus status;// 主要构造函数public Order(Long id, String orderNumber, BigDecimal amount) {this.id = Objects.requireNonNull(id);this.orderNumber = Objects.requireNonNull(orderNumber);this.amount = Objects.requireNonNull(amount);this.status = OrderStatus.CREATED;}// 静态工厂方法提供语义化创建public static Order newOrder(String orderNumber, BigDecimal amount) {return new Order(IdGenerator.next(), orderNumber, amount);}// 业务方法而非setterpublic void confirm() {this.status = OrderStatus.CONFIRMED;}// getterspublic Long getId() { return id; }public String getOrderNumber() { return orderNumber; }public BigDecimal getAmount() { return amount; }public OrderStatus getStatus() { return status; }
}
11. 命名规范
原则:
- 文件名与类名一致:Java源文件名必须与公共类名完全一致;
- 包名规范:使用小写字母,用点分隔,体现项目结构;
- 目录结构清晰:按照包结构组织源文件目录
- 类名使用大驼峰:首字母大写,每个单词首字母大写
- 方法名使用小驼峰:首字母小写,每个单词首字母大写
- 变量名表意明确:避免使用单字母或无意义的缩写
- 常量全大写:使用下划线分隔单词
// 不推荐:命名不规范
public class user_service {private string user_name;private int userage;public void GET_USER() {// ...}
}// 推荐:规范命名
public class UserService {private String userName;private int userAge;private static final int MAX_RETRY_COUNT = 3;public void getUser() {// ...}
}// 不推荐:变量名无意义
public void processData() {int d; // 什么的天数?String s; // 什么字符串?List a; // 什么列表?
}// 推荐:表意明确的命名
public void processData() {int timeoutDays;String errorMessage;List<User> activeUsers;
}// 不推荐:常量命名不规范
private static final int maxcount = 100;
private static final String default_url = "http://localhost";// 推荐:常量规范命名
private static final int MAX_COUNT = 100;
private static final String DEFAULT_URL = "http://localhost";
12. 代码格式
原则:
- 统一缩进:使用4个空格进行缩进,不使用tab
- 合理空行:方法之间、逻辑块之间使用空行分隔
- 规范注释:公共类、方法必须有JavaDoc注释
- 空格规范:操作符、关键字周围使用适当空格
// 不推荐:缩进不一致
public class BadFormatting {
public void method(){
if(condition){
doSomething();
}
}
}// 推荐:统一4空格缩进
public class GoodFormatting {public void method() {if (condition) {doSomething();}}
}// 不推荐:缺少空行分隔
public class BadSpacing {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}// 推荐:合理使用空行
public class GoodSpacing {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}// 不推荐:缺少注释
public class BadDocumentation {public User findUser(Long id) {// 实现return null;}
}// 推荐:完善的注释
/*** 用户服务类,提供用户相关的业务操作* * @author Developer* @since 1.0.0*/
public class GoodDocumentation {/*** 根据用户ID查找用户信息* * @param id 用户ID,必须大于0* @return 用户对象,如果未找到返回null* @throws IllegalArgumentException 当ID小于等于0时抛出*/public User findUser(Long id) {if (id == null || id <= 0) {throw new IllegalArgumentException("Invalid user ID: " + id);}// 实现return null;}
}// 不推荐:空格使用不规范
public void badSpacing(){
int a=1+2;
boolean result=(a>1)?true:false;
}// 推荐:规范的空格使用
public void goodSpacing() {int a = 1 + 2;boolean result = (a > 1) ? true : false;
}
参考文章:https://developer.aliyun.com/ebook/386/read?spm=a2c6h.26392459.ebook-detail.2.63392867ZUEhqw