Jtoss Jtoss
首页
  • 数据结构与算法

    • 数据结构与算法 - 概述
    • 数据结构与算法 - 复杂度分析
    • 数据结构 - 线性表
    • 算法 - 常见排序算法
  • 代码规范

    • 代码简洁之道
    • 阿里巴巴开发手册
    • 谷歌Java编程风格指南
  • 设计模式

    • 编写高质量代码概述
    • 面向对象
    • 设计原则
    • 设计模式-创建型
    • 设计模式-结构型
    • 设计模式-行为型(上)
    • 设计模式-行为型(下)
    • 浅析框架源码中的设计模式
    • 业务框架实战案例
  • MySQL 基础

    • MySQL - 数据库设计规范
    • MySQL - 必知必会
  • MySQL 进阶

    • MySQL - 基础架构
    • MySQL - InnoDB存储引擎
    • MySQL - InnoDB缓冲池
    • MySQL - 事务与锁
    • MySQL - 索引
    • MySQL - 查询执行计划
    • MySQL - 性能优化
  • Redis 系列

    • Redis入门 - 基础相关
    • Redis进阶 - 数据结构
    • Redis进阶 - 持久化RDB和AOF
    • Redis进阶 - 事件机制
    • Redis进阶 - 事务
    • Redis进阶 - 高可用高可扩展
    • Redis进阶 - 缓存问题
    • Redis进阶 - 性能调优
  • Java 基础

    • Java 基础 - 知识点
    • Java 基础 - 面向对象
    • Java 基础 - Q/A
  • Java 进阶 - 集合框架

    • Java 集合框架详解
  • Java 进阶 - 多线程与并发

    • Java 并发 - 理论基础
    • Java 并发 - 线程基础
    • Java 并发 - 各种锁
    • Java 并发 - 关键字 volatile
    • Java 并发 - 关键字 synchronized
    • JUC - CAS与原子操作
    • JUC - 锁核心类AQS
    • JUC - 锁接口和类简介
    • JUC - 并发容器简介
    • JUC - 通信工具类
    • JUC - Fork-Join框架
    • JUC - 线程池
  • Java 进阶 - JVM

    • JVM - 概述
    • JVM - 类加载机制
    • JVM - 内存结构
    • JVM - 垃圾回收机制
    • JVM - 性能调优
  • Maven系列

    • Maven基础知识
    • Maven项目构建
    • Maven多模块配置
  • Spring 框架

    • Spring 框架 - 框架介绍
    • Spring 框架 - IOC详解
    • Spring 框架 - AOP详解
    • Spring 框架 - SpringMVC详解
  • Spring Boot 系列

    • Spring Boot - 开发入门
    • Spring Boot - 接口相关
  • Spring Cloud 系列
  • Mybatis 系列

    • Mybatis - 总体框架设计
    • Mybatis - 初始化基本过程
    • Mybatis - sqlSession执行过程
    • Mybatis - 插件机制
    • Mybatis - 事务管理机制
    • Mybatis - 缓存机制
  • 业务常见问题

    • Java 业务开发常见错误(一)
    • Java 业务开发常见错误(二)
    • Java 业务开发常见错误(三)
    • Java 业务开发常见错误(四)
    • Java 业务开发常见错误(五)
    • Java 业务开发常见错误(六)
  • IDEA系列

    • IDEA 2021开发环境配置
    • IDEA 快捷键
  • Git系列

    • git status中文乱码
  • 其他

    • Typora+Picgo 自动上传图片
    • hsdis 和 jitwatch
  • 实用技巧
  • 收藏
  • 摄影
  • 学习
  • 标签
  • 归档

Jason Huang

后端程序猿
首页
  • 数据结构与算法

    • 数据结构与算法 - 概述
    • 数据结构与算法 - 复杂度分析
    • 数据结构 - 线性表
    • 算法 - 常见排序算法
  • 代码规范

    • 代码简洁之道
    • 阿里巴巴开发手册
    • 谷歌Java编程风格指南
  • 设计模式

    • 编写高质量代码概述
    • 面向对象
    • 设计原则
    • 设计模式-创建型
    • 设计模式-结构型
    • 设计模式-行为型(上)
    • 设计模式-行为型(下)
    • 浅析框架源码中的设计模式
    • 业务框架实战案例
  • MySQL 基础

    • MySQL - 数据库设计规范
    • MySQL - 必知必会
  • MySQL 进阶

    • MySQL - 基础架构
    • MySQL - InnoDB存储引擎
    • MySQL - InnoDB缓冲池
    • MySQL - 事务与锁
    • MySQL - 索引
    • MySQL - 查询执行计划
    • MySQL - 性能优化
  • Redis 系列

    • Redis入门 - 基础相关
    • Redis进阶 - 数据结构
    • Redis进阶 - 持久化RDB和AOF
    • Redis进阶 - 事件机制
    • Redis进阶 - 事务
    • Redis进阶 - 高可用高可扩展
    • Redis进阶 - 缓存问题
    • Redis进阶 - 性能调优
  • Java 基础

    • Java 基础 - 知识点
    • Java 基础 - 面向对象
    • Java 基础 - Q/A
  • Java 进阶 - 集合框架

    • Java 集合框架详解
  • Java 进阶 - 多线程与并发

    • Java 并发 - 理论基础
    • Java 并发 - 线程基础
    • Java 并发 - 各种锁
    • Java 并发 - 关键字 volatile
    • Java 并发 - 关键字 synchronized
    • JUC - CAS与原子操作
    • JUC - 锁核心类AQS
    • JUC - 锁接口和类简介
    • JUC - 并发容器简介
    • JUC - 通信工具类
    • JUC - Fork-Join框架
    • JUC - 线程池
  • Java 进阶 - JVM

    • JVM - 概述
    • JVM - 类加载机制
    • JVM - 内存结构
    • JVM - 垃圾回收机制
    • JVM - 性能调优
  • Maven系列

    • Maven基础知识
    • Maven项目构建
    • Maven多模块配置
  • Spring 框架

    • Spring 框架 - 框架介绍
    • Spring 框架 - IOC详解
    • Spring 框架 - AOP详解
    • Spring 框架 - SpringMVC详解
  • Spring Boot 系列

    • Spring Boot - 开发入门
    • Spring Boot - 接口相关
  • Spring Cloud 系列
  • Mybatis 系列

    • Mybatis - 总体框架设计
    • Mybatis - 初始化基本过程
    • Mybatis - sqlSession执行过程
    • Mybatis - 插件机制
    • Mybatis - 事务管理机制
    • Mybatis - 缓存机制
  • 业务常见问题

    • Java 业务开发常见错误(一)
    • Java 业务开发常见错误(二)
    • Java 业务开发常见错误(三)
    • Java 业务开发常见错误(四)
    • Java 业务开发常见错误(五)
    • Java 业务开发常见错误(六)
  • IDEA系列

    • IDEA 2021开发环境配置
    • IDEA 快捷键
  • Git系列

    • git status中文乱码
  • 其他

    • Typora+Picgo 自动上传图片
    • hsdis 和 jitwatch
  • 实用技巧
  • 收藏
  • 摄影
  • 学习
  • 标签
  • 归档
  • 项目业务常见问题

  • 项目构建

    • Java项目规约
      • 编程规约
        • 基础类型及操作
        • 集合处理
      • 异常
        • 统一异常处理
        • 错误码设计
      • 日志
        • 统一日志记录格式
        • 日志文件
        • 日志追踪字段 traceId
        • 日志归档
      • 工具篇
      • 设计篇
      • 参考
  • 项目业务
  • 项目构建
Jason
目录

Java项目规约

# 编程规约

目前大部分团队是使用的阿里巴巴Java开发规范,不过在日常开发中难免遇到覆盖不到的场景,本文在阿里巴巴Java开发规范基础上,补充一些常用的规范,用于提升代码质量及增强代码可读性。阿里巴巴开发手册 (opens new window)

# 基础类型及操作

  1. 基本类型转换

    字符串转数字时,使用 org.apache.commons.lang3.math.NumberUtils, 优点是可以设置默认值,转换出错时可以返回默认值(默认返回0)。

    Integer i = NumberUtils.toInt("1");
    Integer i = NumebrUtils.toInt("1", 0);
    
    1
    2

    拆箱:当包装类转换为基本类型时需要判断是否为 null:

    Integer i = paras.get(0);
    int j = i != null ? i : 0;
    
    1
    2
  2. 对象类型转换

    使用MapStruct工具(官方文档 (opens new window)),转换类后缀Convertor,所有转换操作都在转换类中操作,禁止在业务代码中编写大量set代码。或者看这篇 (opens new window)

  3. 判断

    • 枚举判定

      使用枚举判等,而不是枚举中的数字。因为枚举更加直观,方便查看以及调试,而数字容易出错。使用 == 优于 equals,具体可参考这两个链接:https://medium.com/danonrockstar/comparing-enum-values-in-java-85e484a9190c 和 https://stackoverflow.com/questions/1750435/comparing-java-enum-members-or-equals;而从 Enums 的源码中可以看到其重写了 equals 方法,也是使用 == 进行判等。

    • 判空

      各种对象判空:

      // 对象判空和非空
      Objects.isNull();
      Objects.nonNull();
      
      //String判空&非空
      StringUtils.isEmpty()   //可匹配null和空字符串
      StringUtils.isNotEmpty()
      StringUtils.isBlank()   //可匹配null、空字符串、多个空白字符
      StringUtils.isNotBlank()
      StringUtils.hasText() // 非null,非空字符串,非空白字符(如空格、tab、换行符等)
      
      //集合判空&非空
      CollectionUtils.isEmpty()
      CollectionUtils.isNotEmpty()
      
      //Map判空&非空
      MapUtils.isEmpty()
      MapUtils.isNotEmpty()
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
    • 断言

      使用 Guava 工具中的 Preconditionis 工具类:

      // 如果为 null 则抛出异常
      Preconditions.checkNotNull(...)
      //通用判断
      Preconditions.checkArgument(...)
      
      1
      2
      3
      4

# 集合处理

  1. Map 快捷操作

    推荐:

    // 如果值不存则计算
    map.computeIfAbsent("key", k -> execValue(k));
    // 默认值
    map.getOrDefault("key", "default");
    
    1
    2
    3
    4

    反例:

    //如果值不存在则计算
    String v = map.get("key");
    if(v == null){
        v = execValue("key");
        map.put("key", v);
    }
    //默认值
    map.containsKey("key") ? map.get("key") : DEFAULT_VALUE
    
    1
    2
    3
    4
    5
    6
    7
    8
  2. 创建对象

    构造方法或 Builder 模式,超过 3 个参数对象创建,使用 Builder 模式

    //Java11+:
    List.of(1, 2, 3)  
    Set.of(1, 2, 3)
    Map.of("a", 1)
    
    //Java8中不可变集合(需引入Guava)
    ImmutableList.of(1,2,3)
    ImmutableSet.of(1,2,3)
    ImmutableMap.of("key","value")
    //多值情况
    ImmutableMap.builder()
        .put("key", "value")
        .put("key2", "value2")
        .build()
    
    //Java8中可变集合(需引入Guava)
    Lists.newArrayList(1, 2, 3)
    Sets.newHashSet(1, 2, 3)
    Maps.newHashMap("key", "value")
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    反例:

    new ArrayList<>(){{
       add(1);
       add(2);
    }};
    
    1
    2
    3
    4
  3. 集合嵌套

    集合里的值如果是基础类型必须加上注释,说明集合里存的是什么,比如:

    //返回值: Map(key: 姓名, value: List(商品))
    Map<String, List<String>> res;
    
    1
    2

    超过 2 层集合对象封装必须封装成自定义类:

    //推荐
    Map<String, List<Node>> res;
    
    @Value
    public static class Node {
        /**
        * 备注说明字段
        */
        String name;
        /**
        * 备注说明字段2
        */
        List<Integer> subjectIds;
    }
    
    //反例
    Map<String, List<Pair<String, List<Integer>>>> res;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

# 异常

# 统一异常处理

# 错误码设计

# 日志

# 统一日志记录格式

# 日志文件

# 日志追踪字段 traceId

微服务全链路

# 日志归档

日志查看 ELK

# 工具篇

  1. JSON

    推荐:使用 Gson 或 Jackson

    不推荐:FastJson,bug 多

  2. 对象

    推荐:

    • MapStruct,根据注解编译成 Java 代码,没有反射,速度快;行为可预测,可查看编译后的 Java 代码查看转换逻辑;运行速度与硬编码差不多,因为在编译期间就生成了 Java Bean 属性复制的代码,运行期间就无需使用反射或者字节码技术,所以确保了高性能。支持深拷贝
    • Spring BeanUtils 内部使用了缓存,加快转换的速度,浅拷贝
    • Cglib BeanCopier 使用字节码技术动态生成一个代理类,代理类实现get 和 set方法,缓存代理类重复使用;性能优于 Spring BeanUtils;浅拷贝
    • orika 重量级;底层其使用了 javassist 生成字段属性的映射的字节码,然后直接动态加载执行字节码文件;性能好;支持深拷贝

    不推荐:

    • Apache BeanUtils 大量反射,性能差;
    • Dozer 重量级;使用反射;性能差;不再维护;深拷贝

    不推荐:超过3个字段手动转换;

  3. 模板代码

    推荐:Lombok,减少代码行数,提升开发效率,自动生成 Java 代码,没有性能损耗;

    不推荐:手动生成大量 set、get 方法;

  4. 参数校验

    推荐:hibernate Validation、spring-boot-starter-validation,可通过注解自动实现参数拦截;

    不推荐:每个入口(比如Controller)都 copy 大量重复的校验逻辑;

  5. 缓存

    推荐:Spring Cache,通过注解控制缓存逻辑,适合常用的加缓存场景。

# 设计篇

  1. 正向语义

    正向语义的好处在于使代码容易理解。 比如:if(judge()){…},很容易理解,即:判定成功则执行代码块。

    相反,如果是负向语义,思维还要转换一下,一般用于方法前置的参数校验。

    正向语义的应用场景有:

    • 方法定义:方法名推荐:canPass、checkParam,返回 true 代表成功。 不推荐:比如 isInvalidParam 返回 true代表失败,增加理解成本;
    • Lambda 表达式:filter 操作符中返回 true 是可以通过的元素;
    • if 和三目运算符:condition ? doSomething() : doSomething2() , 条件判定后紧跟的是判定成功后执行的操作。
  2. 防御式编程

    1)外部传过来数据都需要校验,一般分为两类:

    • 数据流入:用户 Http 请求、RPC 请求、MQ 消费者等
    • 数据依赖:依赖的第三方 RPC、数据库等

    如果是数据流入,一定要首先校验数据合法性再往下执行,推荐 hibernate Validation 这类工具,可以很方便的做数据校验

    如果是数据依赖,一定要考虑各种网络、限流、背压等场景,做好熔断、降级保障。推荐建立防腐层(Anti-Corruption Layer),将第三方的限界上下文语义转换为当前上下文语义,避免理解上的歧义;

    2)Null 处理

    • 对于强依赖,没有返回值不行(比如查询数据库):直接抛异常;

    • 需要反馈给上层处理:

      (1)可能返回 null 的场景:使用 Optional;

      (2)上层需要感知信息异常信息:使用 vavr 中的 Either;

    • 可降级:

      (1)返回值是默认值:集合类返回,数字返回 0 或 -1,字符串返回空字符串,其他场景自定义

      集合默认值:

    Collections.emptyList()  // 空 List
    Collections.emptySet()   // 空 Set
    Collections.emptyMap()   // 空 Map
    
    1
    2
    3

# 参考

  • 作者:木小丰 https://lesofn.com/archives/java-coding-standard
#Java项目规约
上次更新: 2023-12-23
Java 业务开发常见错误(六)

← Java 业务开发常见错误(六)

最近更新
01
开始
01-09
02
AI工具分享
01-09
03
AI 导读
01-07
更多文章>
Theme by Vdoing | Copyright © 2022-2025 Jason Huang | 闽ICP备2025088096号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式