常用编程技巧和代码规范总结

一、前言

April(四月) 是以我的猫咪命名的一个开源团队,创立初衷是为了孵化一些项目到社区,意味着后续也会接受来自其他代码贡献者的代码,但是如果代码贡献者的编程风格与 April 的不一致,会给代码阅读者和其他代码提交者造成不小的困扰. April因此总结了这份编程风格指南, 使所有提交代码的人都能获知 April 的编程风格.

二、Java 编码规范

Java 编程规范主要以阿里巴巴代码规约为主 ( GitHub 无法预览 PDF,可以 clone 本仓库,使用 typora 可以以 PDF 方式查看下面的文档)

三、编程技巧(补充)

一、注释规范

  • 禁用行尾注释
  • 方法或常量,成员变量,禁单行注释,应使用文档注释

注释配置

File | Settings | Editor | File and Code Templates,依次配置 Files 下面的 Class interface enum record @interface

  • 类注释模板
1#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
2#parse("File Header.java")
3
4/**
5 * Description: [${END}]
6 * Author: [mobaijun]
7 * Date: [${DATE} ${TIME}]
8 * IntelliJ IDEA Version: [$PRODUCT_NAME 2023.1.4]
9*/
10public class ${NAME} {
11}
  • 枚举注释模板
1#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
2#parse("File Header.java")
3
4/**
5 * Description: [${END}]
6 * Author: [mobaijun]
7 * Date: [${DATE} ${TIME}]
8 * IntelliJ IDEA Version: [$PRODUCT_NAME 2023.1.4]
9*/
10public enum ${NAME} {
11}
  • 接口注释模板
1#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
2#parse("File Header.java")
3
4/**
5 * Description: [${END}]
6 * Author: [mobaijun]
7 * Date: [${DATE} ${TIME}]
8 * IntelliJ IDEA Version: [$PRODUCT_NAME 2023.1.4]
9*/
10public interface ${NAME} {
11}
  • 注解注释模板
1#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
2#parse("File Header.java")
3
4/**
5 * Description: [${END}]
6 * Author: [mobaijun]
7 * Date: [${DATE} ${TIME}]
8 * IntelliJ IDEA Version: [$PRODUCT_NAME 2023.1.4]
9*/
10public @interface ${NAME} {
11}
  • 安全类
1#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
2#parse("File Header.java")
3
4/**
5 * Description: [${END}]
6 * Author: [mobaijun]
7 * Date: [${DATE} ${TIME}]
8 * IntelliJ IDEA Version: [$PRODUCT_NAME 2023.1.4]
9*/
10public record ${NAME}() {
11}

二、建表规范

  • 遵循三大范式
  • 复杂字段之间用 (_) 下划线相隔,如(create\_timeuser_name
  • 禁止使用外键关联
  • 主键字段使用(bigint)类型,Java 对应类型使用 Long 类型
  • 日期类型字段是 (datetime),Java对应 LocalDateTime 类型

三、查询规范

  • 列表查询

所有的列表查询都需要添加排序,已最后添加的数据显示在第一列,以 date_time 类型作为排序字段,如(创建时间create_time

  • 操作集合尽量使用 stream lambda 表达式,工具类地址(com.mobaijun.common.collection.StreamUtil

四、返回规范

  • 项目中定义了R返回类,目录地址(com.mobaijun.common.result

    • R.ok(T) 成功返回
    • R.ok(T data, String message) 成功返回自定义消息
    • R.failed(HttpStatus failMsg); 失败返回自定义状态码
    • R.failed(HttpStatus failMsg, String message); 失败返回自定义状态码和自定义消息

    返回示例:

    1成功返回
    2{
    3    "code": 200,
    4    "message":"成功返回!",
    5    "data":{
    6        "key":"1",
    7        "value":"2"
    8    }
    9}
    10失败返回
    11{
    12    "code": 20004,
    13    "message":"失败返回!",
    14    "data":{null}
    15}
  • 项目中返回只能在 controller 层进行操作,禁止在业务层(service)返回

  • 业务层如果需要异常处理,使用 throw new Exception("") 进行处理;


五、增删改查返回规范

  • 新增:返回 boolean 类型或对象
  • 修改:返回 boolean 类型或对象
  • 删除:返回 int 类型
  • 查询:返回 List 类型或 Entity 类型
  • 批量:返回 int 类型

六、接口规范

  • 类定义信息为 @Api(tags = {"一级目录-二级目录-业务类型"}, description = "具体描述")
  • 查询使用:@GetMapping(value = "/${methodName}")
  • 新增使用:@PostMapping(value = "/${methodName}")
  • 修改使用:@PutMapping(value = "/${methodName}")
  • 单个删除:@DeleteMapping(value = "/${methodName}")
  • 批量删除:@DeleteMapping(value = "/${methodName}")

命名规则:

  • 单个删除(singleDelete)
  • 批量删除(batchDelete)
  • 新增 (insert[Entity])
  • 修改 (update[Entity])
  • 查询 (select[Entity]List)

七、枚举定义规范

  1. 枚举如果没有set方法,属性需要使用 final 定义;
  2. 枚举每个字段需包含文档注释
  3. 枚举属性全部定义为大写,多个单纯之间以下划线分割
1@Getter
2@AllArgsConstructor
3public enum NameType {
4    /**
5     * 名称
6     */
7    FACTORY_NAME("name");
8    
9    /**
10     * 值
11     */
12    private final String value;
13}

八、编码技巧

成员变量

  • 成员变量禁用 idea 告警关键字,例如
    • width、height

异常处理

如遇到多资源关闭应使用(try-with-resources)语法

1// 代码示例
2public void readFile() throws FileNotFoundException {
3    try (FileReader fr = new FileReader("d:/input.txt");BufferedReader br = new BufferedReader(fr)) {
4        String s = "";
5        while ((s = br.readLine()) != null) {
6            System.out.println(s);
7        }
8    } catch (IOException e) {
9        e.printStackTrace();
10    }
11}

工具类使用:

非必要不新增工具类,以 kjs-common 包工具类为准,大多数场景已经可以完全应付

1<!--引入示例-->
2<dependency>
3    <groupId>com.mobaijun</groupId>
4    <artifactId>kjs-common</artifactId>
5    <version>${latest version}</version>
6</dependency>
  • 依赖关系图

1

集合处理

集合处理使用 Java 8 新特性 lambda 结合 Stream 操作,例如:

1public static void writeWallpaper(List<WallpaperData> wallpaperDataList) throws IOException {
2    if (!Files.exists(WALLPAPER_PATH)) {
3        Files.createFile(WALLPAPER_PATH);
4    }
5    // 扫描本地文件
6    List<WallpaperData> data = readWallpaperData();
7    if (CollUtil.isNotEmpty(data)) {
8        wallpaperDataList.addAll(data);
9    }
10    // 排序
11    List<WallpaperData> collect = wallpaperDataList.stream()
12            // 倒序,最新日期排前面
13            .sorted(Comparator.comparing(WallpaperData::getCreatedAt).reversed())
14            .collect(Collectors.toList());
15    Files.write(WALLPAPER_PATH, "## Wallpaper".getBytes());
16    Files.write(WALLPAPER_PATH, System.lineSeparator().getBytes(), StandardOpenOption.APPEND);
17    collect.forEach(wallpaperData -> {
18        try {
19            Files.write(WALLPAPER_PATH, wallpaperData.formatMarkdown().getBytes(), StandardOpenOption.APPEND);
20            Files.write(WALLPAPER_PATH, System.lineSeparator().getBytes(), StandardOpenOption.APPEND);
21            Files.write(WALLPAPER_PATH, System.lineSeparator().getBytes(), StandardOpenOption.APPEND);
22        } catch (IOException e) {
23            log.error(e.getMessage(), "Failed to write wallpaper.md file");
24        }
25    });
26}

上方代码来自 april-wallpaper 项目

对象实体转换

对象之间的属性赋值应该使用 mapstruct 进行转换,示例代码:

1@PostMapping("/insertMarketIndex")
2public AbstractTip<Integer> insertMarketIndex(@Validated MarketIndexDTO dto) {
3    if (ObjectUtils.isEmpty(dto)) {
4        return new ErrorTip<>("The data is null , abnormal, burst out NullPointerException");
5    }
6    return new SuccessTip<>(marketIndexService.insertMarketIndex(MarketIndexConverter.INSTANCE.toDto(dto)));
7}

九丶判断规范

1/**
2 * 判断规范示例
3 * 
4 * 1. 布尔表达式命名规范
5 *    - 命名应当清晰、具有描述性
6 *    - 避免使用单个字母或缩写,除非是广泛接受的约定(例如,使用 "isEnabled()" 而不是 "isE()")
7 *    - 使用驼峰命名法
8 *
9 * 2. 判断语句格式规范
10 *    - 使用大括号{}括起来判断体,即使只有一条语句
11 *    - 使用缩进使代码块更易读
12 *
13 * 3. 避免冗余的判断
14 *    - 不要使用多余的嵌套判断,保持代码的简洁性
15 *    - 考虑使用逻辑运算符(&&、||)来合并条件
16 *
17 * 4. 注意空指针判断
18 *    - 在可能出现空指针的地方进行判断,避免空指针异常
19 *    - 使用 Objects.requireNonNull 或 Optional 类来处理可能的空值
20 *
21 * 5. 使用 equals 方法进行对象比较
22 *    - 避免使用 "==" 运算符进行对象比较,应当使用 equals 方法
23 *    - 如果需要比较基本数据类型,可以使用 "==" 运算符
24 *
25 * 6. 使用 switch 语句的时候注意 break
26 *    - 在 switch 语句中,每个 case 语句结束后都要添加 break,防止穿透到下一个 case
27 *
28 * 7. 注释规范
29 *    - 在复杂的判断逻辑处添加注释,解释判断的目的和可能的结果
30 *    - 注释要保持更新,确保它们反映代码的实际状态
31 *
32 * 8. 单元测试
33 *    - 编写相应的单元测试来验证判断的正确性
34 *    - 考虑使用断言来验证判断的期望结果
35 *
36 * 9. 集合判断规范
37 *    - 在判断集合是否为空时,使用集合类的 isEmpty() 方法
38 *    - 避免使用 size() 方法判断集合是否为空,因为它可能引起不必要的遍历
39 *    - 示例:if (list.isEmpty()) { /* 集合为空的处理 */ }
40 */

示例代码:

1import java.util.List;
2
3/**
4 * 判断规范示例(包含集合判断)
5 * 
6 * <p>
7 * 该类演示了在Java中编写判断语句的规范。其中包含布尔表达式的命名规范、判断语句的格式规范、避免冗余的判断、
8 * 注意空指针判断、使用 equals 方法进行对象比较、使用 switch 语句的时候注意 break、注释规范、单元测试等内容。
9 * </p>
10 * 
11 * <p>
12 * 集合判断规范:
13 * 在判断集合是否为空时,使用集合类的 isEmpty() 方法。避免使用 size() 方法判断集合是否为空,因为它可能引起不必要的遍历。
14 * </p>
15 * 
16 * <pre>
17 * 示例代码:
18 * {@code
19 * List<String> stringList = List.of("one", "two", "three");
20 * if (isListEmpty(stringList)) {
21 *     System.out.println("List为空");
22 * } else {
23 *     System.out.println("List不为空");
24 * }
25 * }
26 * </pre>
27 * 
28 * <p>
29 * 使用该类时,请注意替换示例中的具体逻辑和集合类型以适应你的实际项目需求。
30 * </p>
31 * 
32 * @version 1.0
33 * @since 2024-01-29
34 */
35public class JudgmentSpecificationWithCollectionsExample {
36
37    /**
38     * 判断List是否为空的示例方法
39     *
40     * @param list 待判断的List
41     * @return 如果List为空,则返回 true;否则返回 false
42     */
43    private static boolean isListEmpty(List<?> list) {
44        // 示例判断逻辑
45        return list == null || list.isEmpty();
46    }
47
48    /**
49     * 主方法,包含示例代码的执行
50     * 
51     * @param args 命令行参数
52     */
53    public static void main(String[] args) {
54        // 示例代码
55        List<String> stringList = List.of("one", "two", "three");
56
57        if (isListEmpty(stringList)) {
58            System.out.println("List为空");
59        } else {
60            System.out.println("List不为空");
61        }
62    }
63}

四、代码提交

idea 忽略文件:

File | Settings | Editor | File Types

忽略掉 |*.iml |.idea |.mvn |.mvnw |mvnw |mvnw.cmd |target

提交规范

主要以 GitMoji 规范为主,gitmoji 是一个标准化和解释在GitHub提交消息上使用 emoji 的倡议。 gitmoji 是一个开源项目,专门规定了在 github 提交代码时应当遵循的 emoji 规范,在 git commit上使用 emoji 提供了一种简单的方法,仅通过查看所使用的表情符号来确定提交的目的或意图。

在执行 git commit 指令时使用 emoji 图标为本次提交添加一个特别的图标, 这个本次提交的记录很容易突出重点,或者说光看图标就知道本次提交的目的。这样就方便在日后查看历史提交日子记录中快速的查找到对于的提交版本。由于有很多不同的表情符号,表情库更新后,没有一个可以帮助更轻松地使用表情符号的中文表情库列表。

提交示例,图标地址传送门