66-代码规范详解

2 阅读6分钟

代码规范详解

一、知识概述

代码规范是团队协作的基础,统一的代码风格能提高代码可读性、降低维护成本、减少Bug产生。阿里巴巴Java开发手册是国内Java开发者最广泛遵循的规范之一,配合SonarQube等静态代码分析工具,可以有效提升代码质量。

本文将深入分析代码规范的核心要点,包括阿里巴巴规范、SonarQube配置、Checkstyle集成、IDE格式化配置等内容。

代码规范的核心价值

  1. 可读性:统一风格,易于阅读理解
  2. 可维护性:降低修改成本,减少引入Bug
  3. 团队协作:减少代码审查争议
  4. 质量保障:静态分析发现潜在问题

二、知识点详细讲解

2.1 阿里巴巴Java开发规范

命名规范
/**
 * 命名规范示例
 */
public class NamingConventions {
    
    // ==================== 类名:UpperCamelCase ====================
    // ✓ 正确
    public class UserController {}
    public class OrderService {}
    
    // ✗ 错误
    // public class userController {}
    // public class order_service {}
    
    // ==================== 方法名、参数名、成员变量:lowerCamelCase ====================
    // ✓ 正确
    private String userName;
    private int orderCount;
    
    public void calculateTotalPrice() {}
    public User findUserById(Long userId) { return null; }
    
    // ✗ 错误
    // private String user_name;
    // public void CalculateTotalPrice() {}
    
    // ==================== 常量:UPPER_SNAKE_CASE ====================
    // ✓ 正确
    public static final int MAX_RETRY_COUNT = 3;
    public static final String DEFAULT_CHARSET = "UTF-8";
    
    // ✗ 错误
    // public static final int maxRetryCount = 3;
    
    // ==================== 抽象类命名 ====================
    // ✓ 正确:以Abstract或Base开头
    public abstract class AbstractDao {}
    public abstract class BaseService {}
    
    // ==================== 异常类命名 ====================
    // ✓ 正确:以Exception结尾
    public class BusinessException extends RuntimeException {}
    public class ValidationException extends IllegalArgumentException {}
    
    // ==================== 测试类命名 ====================
    // ✓ 正确:以Test结尾
    public class UserServiceTest {}
    public class OrderControllerTest {}
    
    // ==================== 包名:全小写 ====================
    // ✓ 正确
    // package com.example.order.service;
    
    // ✗ 错误
    // package com.example.Order.Service;
    
    // ==================== 枚举类 ====================
    public enum UserStatus {
        ACTIVE,         // 枚举值全大写
        INACTIVE,
        DELETED;
        
        // 枚举成员变量
        private final String description;
        
        UserStatus() {
            this.description = this.name().toLowerCase();
        }
    }
}

/**
 * POJO类命名规范
 */
// DO (Data Object):数据库映射对象
public class UserDO {
    private Long id;
    private String name;
    private LocalDateTime createTime;
}

// DTO (Data Transfer Object):数据传输对象
public class UserDTO {
    private String name;
    private String email;
}

// VO (View Object):视图对象
public class UserVO {
    private String displayName;
    private String avatarUrl;
}

// BO (Business Object):业务对象
public class OrderBO {
    private Long orderId;
    private List<OrderItem> items;
    private BigDecimal totalAmount;
}

// AO (Application Object):应用对象
public class UserAO {
    private UserDTO user;
    private List<RoleDTO> roles;
}
常量定义
/**
 * 常量定义规范
 */
public class ConstantDefinitions {
    
    // ==================== 不允许魔法值直接出现在代码中 ====================
    // ✗ 错误
    // if (status == 1) { ... }
    // if ("admin".equals(role)) { ... }
    
    // ✓ 正确:使用常量
    private static final int STATUS_ACTIVE = 1;
    private static final String ROLE_ADMIN = "admin";
    
    // ==================== Long类型常量后缀 ====================
    // ✓ 正确:使用L后缀(大写)
    private static final Long MAX_ID = 10000000000L;
    
    // ✗ 错误:使用l后缀(小写,容易混淆)
    // private static final Long MAX_ID = 10000000000l;
    
    // ==================== 常量按功能分类 ====================
    /**
     * 订单状态常量
     */
    public static class OrderStatus {
        public static final int PENDING = 0;
        public static final int PAID = 1;
        public static final int SHIPPED = 2;
        public static final int COMPLETED = 3;
        public static final int CANCELLED = 4;
    }
    
    /**
     * 缓存Key常量
     */
    public static class CacheKeys {
        public static final String USER_PREFIX = "user:";
        public static final String ORDER_PREFIX = "order:";
        public static final int DEFAULT_EXPIRE = 3600;
    }
}
OOP规约
/**
 * OOP规约示例
 */
public class OOPGuidelines {
    
    // ==================== 避免通过对象引用访问静态变量或方法 ====================
    public static final int MAX_COUNT = 100;
    
    public static int calculate() {
        return MAX_COUNT * 2;
    }
    
    public void test() {
        // ✗ 错误
        // System.out.println(this.MAX_COUNT);
        // System.out.println(this.calculate());
        
        // ✓ 正确
        System.out.println(OOPGuidelines.MAX_COUNT);
        System.out.println(OOPGuidelines.calculate());
    }
    
    // ==================== 所有的覆写方法必须加@Override注解 ====================
    @Override
    public String toString() {
        return "OOPGuidelines";
    }
    
    @Override
    public boolean equals(Object obj) {
        return super.equals(obj);
    }
    
    // ==================== 相同参数类型,相同业务含义,才可使用可变参数 ====================
    // ✓ 正确
    public int sum(int... numbers) {
        int total = 0;
        for (int num : numbers) {
            total += num;
        }
        return total;
    }
    
    // ✗ 错误:不同含义的参数不应使用可变参数
    // public void updateUser(Long userId, String... fields) {}
    
    // ==================== 外部正在调用或者二方库依赖的接口,不允许修改方法签名 ====================
    // 接口一旦发布,就只能增加新方法,不能修改已有方法
    
    // ==================== 所有整型包装类对象之间值的比较,全部使用equals方法 ====================
    public void compareIntegers() {
        Integer a = 128;
        Integer b = 128;
        
        // ✗ 错误
        // if (a == b) { ... }
        
        // ✓ 正确
        if (a.equals(b)) {
            System.out.println("相等");
        }
    }
}
集合处理
/**
 * 集合处理规范
 */
public class CollectionGuidelines {
    
    // ==================== 集合判空 ====================
    public void checkEmpty(List<String> list) {
        // ✗ 错误
        // if (list == null) { ... }
        
        // ✓ 正确:使用工具类
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
    }
    
    // ==================== 集合转数组 ====================
    public void toArray() {
        List<String> list = Arrays.asList("a", "b", "c");
        
        // ✓ 正确:指定数组长度
        String[] array = list.toArray(new String[0]);
        
        // ✗ 错误:直接使用toArray()返回Object[]
        // Object[] array = list.toArray();
    }
    
    // ==================== 数组转集合 ====================
    public void toList() {
        String[] array = {"a", "b", "c"};
        
        // ✓ 正确:使用Arrays.asList()或new ArrayList<>()
        List<String> list = new ArrayList<>(Arrays.asList(array));
        
        // ⚠️ 注意:Arrays.asList()返回的List是固定大小的
        // List<String> fixedList = Arrays.asList(array);
        // fixedList.add("d"); // UnsupportedOperationException
    }
    
    // ==================== 遍历删除元素 ====================
    public void removeElement() {
        List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
        
        // ✓ 正确方式1:使用Iterator
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String item = iterator.next();
            if ("b".equals(item)) {
                iterator.remove();
            }
        }
        
        // ✓ 正确方式2:Java 8+ removeIf
        list.removeIf(item -> "b".equals(item));
        
        // ✗ 错误:直接在foreach中删除
        // for (String item : list) {
        //     if ("b".equals(item)) {
        //         list.remove(item); // ConcurrentModificationException
        //     }
        // }
    }
    
    // ==================== 集合初始化指定大小 ====================
    public void initCollection() {
        // ✓ 正确:指定初始容量
        List<String> list = new ArrayList<>(100);
        Map<String, String> map = new HashMap<>(32);
        
        // ✗ 错误:默认大小可能导致多次扩容
        // List<String> list = new ArrayList<>();
    }
}
并发处理
/**
 * 并发处理规范
 */
public class ConcurrencyGuidelines {
    
    // ==================== 线程池创建 ====================
    // ✗ 错误:Executors创建线程池
    // ExecutorService executor = Executors.newFixedThreadPool(10);
    
    // ✓ 正确:ThreadPoolExecutor自定义
    ExecutorService executor = new ThreadPoolExecutor(
        5,                      // corePoolSize
        10,                     // maximumPoolSize
        60L,                    // keepAliveTime
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(100),  // 有界队列
        new ThreadFactoryBuilder().setNameFormat("worker-%d").build(),
        new ThreadPoolExecutor.CallerRunsPolicy()
    );
    
    // ==================== SimpleDateFormat线程安全 ====================
    // ✗ 错误:SimpleDateFormat非线程安全
    // private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    
    // ✓ 正确方式1:ThreadLocal
    private static final ThreadLocal<SimpleDateFormat> dateFormat = 
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
    
    // ✓ 正确方式2:DateTimeFormatter(Java 8+)
    private static final DateTimeFormatter formatter = 
        DateTimeFormatter.ofPattern("yyyy-MM-dd");
    
    // ==================== 避免Random实例被多线程使用 ====================
    // ✗ 错误
    // private static final Random random = new Random();
    
    // ✓ 正确:ThreadLocalRandom(Java 7+)
    public int nextRandom() {
        return ThreadLocalRandom.current().nextInt(100);
    }
}

2.2 Checkstyle配置

<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
    "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
    "https://checkstyle.org/dtds/configuration_1_3.dtd">

<module name="Checker">
    <property name="charset" value="UTF-8"/>
    <property name="severity" value="warning"/>
    
    <!-- 文件长度检查 -->
    <module name="FileLength">
        <property name="max" value="2000"/>
    </module>
    
    <!-- 行尾空格检查 -->
    <module name="RegexpSingleline">
        <property name="format" value="\s+$"/>
        <property name="minimum" value="0"/>
        <property name="maximum" value="0"/>
        <property name="message" value="Line has trailing spaces."/>
    </module>
    
    <module name="TreeWalker">
        <!-- 包名检查 -->
        <module name="PackageName">
            <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
        </module>
        
        <!-- 类名检查 -->
        <module name="TypeName">
            <property name="format" value="^[A-Z][a-zA-Z0-9]*$"/>
        </module>
        
        <!-- 方法名检查 -->
        <module name="MethodName">
            <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
        </module>
        
        <!-- 常量名检查 -->
        <module name="ConstantName">
            <property name="format" value="^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$"/>
        </module>
        
        <!-- 方法长度检查 -->
        <module name="MethodLength">
            <property name="max" value="80"/>
        </module>
        
        <!-- 参数个数检查 -->
        <module name="ParameterNumber">
            <property name="max" value="7"/>
        </module>
        
        <!-- 避免魔法数字 -->
        <module name="MagicNumber">
            <property name="ignoreNumbers" value="-1, 0, 1, 2"/>
        </module>
        
        <!-- 缩进检查 -->
        <module name="Indentation">
            <property name="basicOffset" value="4"/>
            <property name="braceAdjustment" value="0"/>
        </module>
        
        <!-- 空格检查 -->
        <module name="EmptyForIteratorPad"/>
        <module name="MethodParamPad"/>
        <module name="NoWhitespaceAfter"/>
        <module name="NoWhitespaceBefore"/>
        <module name="OperatorWrap"/>
        <module name="ParenPad"/>
        <module name="TypecastParenPad"/>
        <module name="WhitespaceAfter"/>
        <module name="WhitespaceAround"/>
        
        <!-- 导入检查 -->
        <module name="AvoidStarImport"/>
        <module name="IllegalImport"/>
        <module name="RedundantImport"/>
        <module name="UnusedImports"/>
        
        <!-- 注释检查 -->
        <module name="JavadocMethod">
            <property name="scope" value="public"/>
        </module>
        <module name="JavadocType"/>
        <module name="JavadocVariable"/>
        
        <!-- 代码复杂度检查 -->
        <module name="CyclomaticComplexity">
            <property name="max" value="10"/>
        </module>
    </module>
</module>

2.3 SonarQube配置

<!-- pom.xml配置 -->
<plugin>
    <groupId>org.sonarsource.scanner.maven</groupId>
    <artifactId>sonar-maven-plugin</artifactId>
    <version>3.10.0.2594</version>
</plugin>

<!-- sonar-project.properties -->
# 项目配置
sonar.projectKey=my-project
sonar.projectName=My Project
sonar.projectVersion=1.0.0

# 源码目录
sonar.sources=src/main/java
sonar.tests=src/test/java
sonar.java.binaries=target/classes
sonar.java.test.binaries=target/test-classes

# 排除目录
sonar.exclusions=**/generated/**,**/dto/**,**/entity/**

# 代码覆盖率
sonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml

# 质量门禁
sonar.qualitygate.wait=true

三、总结与最佳实践

3.1 规范执行清单

  1. 命名规范:统一风格,见名知意
  2. 常量定义:拒绝魔法值,集中管理
  3. OOP规约:合理继承,避免滥用
  4. 集合处理:判空、转数组、遍历删除
  5. 并发处理:线程安全,资源管理

3.2 工具集成

  • IDE:IDEA/Eclipse格式化配置
  • 构建工具:Maven Checkstyle/SonarQube插件
  • CI/CD:代码质量门禁

参考资料

  1. 阿里巴巴Java开发手册
  2. Checkstyle官方文档
  3. SonarQube官方文档
  4. Google Java Style Guide

六、思考与练习

思考题

  1. 基础题:阿里巴巴规范中,类名、方法名、常量名分别应该使用什么命名风格?POJO类中DO、DTO、VO、BO分别代表什么含义?
  2. 进阶题:为什么禁止在代码中直接使用魔法值?Long类型常量为什么要使用大写L后缀而不是小写l?
  3. 实战题:如何在团队中推广代码规范?如何平衡规范的严格执行和开发效率?

编程练习

练习:对一个现有的Java项目运行Checkstyle检查,找出所有违反规范的地方,逐一修复并提交代码,确保所有检查项都通过。

章节关联

  • 前置章节:单元测试详解
  • 后续章节:代码审查实践
  • 扩展阅读:阿里巴巴Java开发手册、Google Java Style Guide

📝 下一章预告

代码审查是保证代码质量的重要环节。下一章将介绍代码审查的最佳实践,包括审查流程、审查清单、常见问题模式,帮助你提升团队协作效率。


本章完