目前大部分团队是应用的阿里巴巴 Java 开发标准,不过在日常开发中不免遇到笼罩不到的场景,本文在阿里巴巴 Java 开发标准根底上,补充一些罕用的标准,用于晋升代码品质及加强代码可读性。
编程规约
1、根底类型及操作
(1)转换
根本类型转换
String 类型转数字:应用 apache common-lang3 包中的工具类 NumberUtils,劣势:可设置默认值,转换出错时返回默认值
NumberUtils.toInt("1");
拆箱:包装类转化为根本类型的时候,须要断定 null,比方:
Integer numObject = param.get(0);
int num = numObject != null ? numObject : 0;
对象类型转换
应用 MapStruct 工具,转换类后缀 Convertor,所有转换操作都在转换类中操作,禁止在业务代码中编写大量 set 代码。
(2)判断
枚举断定
应用枚举判等,而非枚举对应的数字。因为枚举更直观,不便查看代码及调试,数字容易出错。
判空
各种对象的判空:
// 对象判空 & 非空
Objects.isNull()
Objects.nonNull()
//String 判空 & 非空
StringUtils.isEmpty() // 可匹配 null 和空字符串
StringUtils.isNotEmpty()
StringUtils.isBlank() // 可匹配 null、空字符串、多个空白字符
StringUtils.isNotBlank()
// 汇合判空 & 非空
CollectionUtils.isEmpty()
CollectionUtils.isNotEmpty()
//Map 判空 & 非空
MapUtils.isEmpty()
MapUtils.isNotEmpty()
断言
应用 Guava 里的 Preconditions 工具类,比方:
// 如果是空则抛异样
Preconditions.checkNotNull()
// 通用判断
Preconditions.checkArgument()
2、汇合解决
(1)Map 快捷操作
举荐:
// 如果值不存在则计算
map.computeIfAbsent("key",k-> execValue(k));
// 默认值
map.getOrDefault("key", DEFAULT_VALUE)
反例:
// 如果值不存在则计算
String v = map.get("key");
if(v == null){v = execValue("key");
map.put("key", v);
}
// 默认值
map.containsKey("key") ? map.get("key") : DEFAULT_VALUE
(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")
反例:
new ArrayList<>(){{add(1);
add(2);
}};
(3)汇合嵌套
汇合里的值如果是根底类型必须加上正文,阐明汇合里存的是什么,比方:
// 返回值: Map(key: 姓名, value: List( 商品))
Map<String, List<String>> res;
超过 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、异样
对于异样及错误码的思考,请参考笔者的另一篇文章:错误码设计思考
异样除了抛异样还有一种场景,即:下层发动多个必要调用,某些可能失败,须要下层自行决定解决策略,举荐应用 vavr 中的 Either 类,Either 应用倡议:通常咱们应用左值示意异样,而右值示意失常调用后的返回后果,即: Either<Throwable, Data>
2、日志
(1)日志文件
依据日志等级个别分为 4 个日志文件即可:debug.log、info.log、warn.log、error.log;
如有非凡需要可依据场景独自建文件,比方申请日志:request.log、gc 日志:gc.log 等。
(2)所有用户日志都要有追踪字段
追踪字段包含:traceId、userId 等,举荐应用 MDC,罕用的日志框架:Log4j、Logback 都反对。
(3)日志清理及长久化
本地日志依据磁盘大小,必须设置日志保留天数,否则有硬盘满危险;
分布式环境为了不便查问,须要将日志采集到 ES 中查问;
重要日志:比方审计日志、B 端操作日志须要长久保留,个别是保留到 Hive 中;
工具篇
1、JSON
举荐:应用 Gson 或 Jackson;
不举荐:Fastjson。Fastjson 爆出的破绽多。
2、对象转换
举荐:MapStruct,依据注解编译成 Java 代码,没有反射,速度快;行为可预测,可查看编译后的 Java 代码查看转换逻辑;
不举荐: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() , 条件断定后紧跟的是断定胜利后执行的操作。
反例:
if (!judge()) {doSomething2()
} else {doSomething()
}
2、进攻式编程
(1)内部数据校验
内部传过来数据都须要校验,个别分为两类:
- 数据流入:用户 Http 申请、RPC 申请、MQ 消费者等
- 数据依赖:依赖的第三方 RPC、数据库等
如果是数据流入,肯定要首先校验数据合法性再往下执行,举荐 hibernate Validation 这类工具,能够很不便的做数据校验
数据是数据依赖,肯定要思考各种网络、限流、背压等场景,做好熔断、降级保障。举荐建设防腐层,将第三方的限界上下文语义转换为以后上下文语义,防止了解上的歧义;
(2)Null 解决
- 对于强依赖,没有返回值不行(比方查询数据库):间接抛异样;
-
须要反馈给下层解决:
(1)可能返回 null 的场景:应用 Optional;
(2)下层须要感知信息异样信息:应用 vavr 中的 Either;
-
可降级:
(1)返回值是默认值:汇合类返回,数字返回 0 或 -1,字符串返回空字符串,其余场景自定义
汇合默认值:
Collections.emptyList() // 空 List
Collections.emptySet() // 空 Set
Collections.emptyMap() // 空 Map
总结
本文总结了 Java 开发罕用的高级标准,临时想到这么多,对文章中观点感兴趣,欢送留言或加微信交换。
作者博客链接:Java 研发标准 (进阶版)
作者简介:木小丰,美团 Java 技术专家,专一分享软件研发实际、架构思考。欢送关注公共号:Java 研发
更多精彩文章:
错误码设计思考
Java 线程池进阶
从 MVC 到 DDD 的架构演进
平台化建设思路浅谈
构建可回滚的利用及上线 checklist 实际