基于Java开发手册 2020泰山版
文章目录
- 编码规约
- 命名风格
- 常量定义
- 代码格式
- OOP规约
- 所有的覆写方法, 必须加@Override注解
- 可变参数
- 接口过时必须加@Deprecated 注解
- 使用常量或确定有值的对象来调用 equals
- 整型包装类对象之间值的比较,使用 equals 方法比较
- 任何货币金额,均以最小货币单位且整型类型来进行存储
- 浮点数之间的等值判断,使用BigDecimal判断
- 禁止使用构造方法 BigDecimal(double)的方式把 double 值转化为 BigDecimal 对象
- 所有POJO类属性必须为包装数据类型;所有局部变量推荐使用基本数据类型
- POJO 类不要设定任何属性默认值
- 序列化类新增属性时,请不要修改 serialVersionUID 字段
- POJO 类必须写 toString 方法
- 类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter / setter方法
- getter/setter 方法中,不要增加业务逻辑,增加排查问题的难度
- 循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展
- 慎用 Object 的 clone 方法来拷贝对象
- 日期和时间
- 集合处理
- 并发处理
- 控制语句
- 注释规约
编码规约
命名风格
略
常量定义
略
代码格式
采用 4 个空格缩进,禁止使用 tab 字符
idea默认采用4个空格缩进, 请勿勾选 setting > editor > code style > tabs and indents
中的Use tab character
IDE 中文件的换行符使用 Unix 格式,不要使用Windows格式
idea默认根据系统采用对应的换行符格式, 可以在setting > editor > code style > general
中设置line separator: Unix and macOS(\n)
CR: 回车,MAC老版本的做法,后来的MAC系统统一换成LF了
LF: 换行,Linux下的做法
CRLF: 回车换行,Windows下的做法
OOP规约
所有的覆写方法, 必须加@Override注解
@Override作用
- 准确判断是否覆盖成功, 避免getObject()与 get0bject()的问题
- 如果在抽象类中对方法签名进行修改,其实现类会马上编译报错
可变参数
- 相同参数类型,相同业务含义,才可以使用 Java 的可变参数
- 避免使用 Object
- 可变参数必须放置在参数列表的最后
- 尽量不用可变参数编程
接口过时必须加@Deprecated 注解
@Deprecated 注解
- 可用于类, 方法, 属性上
- 通常在给定此注解后,应该在方法注释中同样说明:废弃此方法后的代替方法是哪个、处理原逻辑代替方案是什么 、本身不打算代替,而是直接清除的,则最好给出会清除此方法的具体代码版本号
- Java 9 中注解增加了两个新元素:
@Deprecated(since = "1.2", forRemoval = true)
- since: 元素指定已注解的API元素已被弃用的版本
- forRemoval: 元素表示注解的 API 元素在将来的版本中被删除,应该迁移 API
使用常量或确定有值的对象来调用 equals
避免空指针异常, 或使用java.util.Objects#equals
整型包装类对象之间值的比较,使用 equals 方法比较
对于 Integer var = ? 在-128 至 127 之间的赋值,Integer 对象是在 IntegerCache.cache 产生,
会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都
会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断
任何货币金额,均以最小货币单位且整型类型来进行存储
- 运算, 整型运算效率更高
- 换算展示方便, 比如展示为20元, 而非20.00元
浮点数之间的等值判断,使用BigDecimal判断
- (1.0f - 0.9f) == (0.9f - 0.8f) //false
禁止使用构造方法 BigDecimal(double)的方式把 double 值转化为 BigDecimal 对象
BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常, 如:
new BigDecimal(0.1); //0.1000000000000000055511151231257827021181583404541015625
推荐使用:
new BigDecimal("0.1")
BigDecimal.valueOf(0.1)
所有POJO类属性必须为包装数据类型;所有局部变量推荐使用基本数据类型
- 数据库的查询结果可能是 null,用基本数据类型接收有 NPE 风险
- 基本数据类型的初值可能对业务逻辑产生影响
POJO 类不要设定任何属性默认值
- 更新时, 可能更新到未改变的默认值属性
序列化类新增属性时,请不要修改 serialVersionUID 字段
- 可序列化的类, 必须定义serialVersionUID值
- serialVersionUID 不一致会抛出序列化运行时异常
POJO 类必须写 toString 方法
- 在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题
类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter / setter方法
公有方法是类的调用者和维护者最关心的方法,首屏展示最好;保护方法虽然只是子类关心,也可能是“模板设计模式”下的核心方法;而私有方法外部一般不需要特别关心,是一个黑盒实现;因为承载的信息价值较低,所有 Service 和 DAO 的 getter/setter 方法放在类体最后
getter/setter 方法中,不要增加业务逻辑,增加排查问题的难度
循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展
//反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行 append
操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费
String str = "start";
for (int i = 0; i < 100; i++) {
str = str + "hello"; }
慎用 Object 的 clone 方法来拷贝对象
- 对象 clone 方法默认是浅拷贝
日期和时间
日期格式化时,传入 pattern 中表示年份统一使用小写的 y
日期格式化时,yyyy 表示当天所在的年,而大写的 YYYY 代表是 week in which year(JDK7 之后引入的概念),意思是当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,返回的 YYYY就是下一年
在日期格式中分清楚大写的 M 和小写的 m,大写的 H 和小写的 h 分别指代的意义
- 表示月份是大写的 M
- 表示分钟则是小写的 m
- 24 小时制的是大写的 H
- 12 小时制的则是小写的 h
获取当前毫秒数:System.currentTimeMillis(); 而不是 new Date().getTime()
//使用System.currentTimeMillis(), 效率更高
public Date() {
this(System.currentTimeMillis());
}
集合处理
list和数组互转
//list转array
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.toArray(new String[0]);
//array转list
//Arrays.asList(strArray)返回值是java.util.Arrays类中一个私有静态内部类java.util.Arrays.ArrayList,它并非java.util.ArrayList类。java.util.Arrays.ArrayList类具有 set(),get(),contains()等方法,但是不具有添加add()或删除remove()方法,所以调用add()方法会报错
String[] strings = {"1", "0"};
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, strings);
不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁
//使用foreach可能抛出ConcurrentModificationException
//应该使用Iterator方式
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
if ("2".equals(str)) {
iterator.remove();
}
}
System.out.println(list);
使用 entrySet或Map.forEach方法遍历Map
//使用Map.forEach方法遍历Map
HashMap<String, String> map = new HashMap<>();
map.put("1", "a");
map.put("2", "b");
map.forEach((k, v) -> System.out.printf("key: %s, value: %s%n", k, v));
并发处理
略
控制语句
在高并发场景中,避免使用”等于”判断作为中断或退出的条件
- 例如: 判断剩余奖品数量等于 0 时,终止发放奖品,但因为并发处理错误导致奖品数量瞬间变成了负数,这样的话,活动无法终止
表达异常的分支时,少用 if-else 方式
卫语句(guard clauses)就是把复杂的条件表达式拆分成多个条件表达式,比如一个很复杂的表达式,嵌套了好几层的if-then-else语句,转换为多个if语句,实现它的逻辑,这多条的if语句就是卫语句
public void findBoyfriend (Man man){
if (man.isUgly()) {
System.out.println("本姑娘是外貌协会的资深会员");
return;
}
if (man.isPoor()) {
System.out.println("贫贱夫妻百事哀");
return;
}
if (man.isBadTemper()) {
System.out.println("银河有多远,你就给我滚多远");
return;
}
System.out.println("可以先交往一段时间看看");
}
不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性
final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
if (existed) {
...
}
避免采用取反逻辑运算符
- 不利于快速阅读
注释规约
略