1. 代码评审的成长

大家好,我是方圆。最近针对近半年的代码评审做了一次总结分享,主要是一些很简单的问题及代码优化注意事项。

2. 详细问题及注意事项

2.1 Integer, Long, String 类型要使用equals来判断相等

为什么IntegerLong多数情况下用==比较结果也正确呢?

  • intInteger类型比较,都为true,因为会把Integer拆箱后再去比
  • 两个非new出来的Integer(eg: Integer a = 5),使用 ==比较且大小在-128 ~ 127之间,则为true,否则为false。
    这个范围可以使用 -XX:AutoBoxCacheMax=?来进行调整
  • 两个Integer进行比较,其中一个是new出来的==比较的话为false,因为==比较在对引用类型比较时比较的是存放在堆中的地址值

2.2 加密异常需要抛出来

这种情况下如果加密出现异常被catch住之后,它还会让代码继续向下执行,所以应该在catch块中把异常抛出来

2.3 double类型加减运算精度丢失问题

这个是老生常谈的问题,在《Effective Java》这本书中有过关于浮点数计算的提醒:浮点数不用于精确计算,可用在科学计数取近似值,所以在代码中进行小数运算要使用BigDecimal

2.4 count(字段)替换为count(0)

如果想在字段值为null的时候同样也计数,需要使用count(0),count(字段)当字段为null时不计数

2.5 事务注解需要加上rollbackFor

如果不加的话,则只针对RuntimeException回滚

2.6 集合转数组

  • 使用集合转数组的方法,必须使用toArray(T[] array),传入类型完全一样的数组
List<Integer> list = new ArrayList<>();
list.add(1);Integer[] ints = (Integer[]) list.toArray(new Integer[0]);

其中数组空间大小的length有以下几种情况

  1. 等于0,动态创建与size相同的数组,性能最好
  2. 大于0但小于size,重新创建大小等于size的数组,增加GC负担
  3. 等于size,在高并发情况下,数组创建完成之后,size正在变大的情况下, 负面影响与2相同
  4. 大于size,空间浪费,且在size处插入null值,存在NPE隐患

2.7 数组转集合

  • 使用Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法(add/remove/clear),否则会抛出UnsupportedOperationException 异常

因为Arrays.asList()使用的是适配器模式,返回的对象是Arrays内部类,它只是做了转换,而后台的数据仍然是数组。

2.8 ArrayList的subList

  • ArrayList的subList结果不可强转成ArrayList,否则会抛出 ClassCastException异常

subList 返回的是ArrayList的内部类SubList,并不是ArrayList而是ArrayList的一个视图,对于SubList子列表的所有操作最终会反映到原列表上

  • subList场景中,高度注意对原列表的修改,否则会导致子列表的遍历、增加、删除产生ConcurrentModificationException异常

ArrayList 创建SubList时,此时它们的modCount值一致。当ArrayList增加或者删除时会修改modCount,这时子列表中的modCount是没有被修改的。所以当子列表遍历时判断subList的modCount和ArrayList的modCount不一致,这就会抛出ConcurrentModificationException异常

2.9 double转换BigDecimal

  • 禁止使用构造方法BigDecimal(double)的方式把double值转化为BigDecimal对象

BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。
如:BigDecimal g = new BigDecimal(0.1f); 实际的存储值为:0. 1000000000000000055511151231257827021181583404541015625

优先使用入参为String的构造方法new BigDecimal("0. 1")BigDecimal.valueOf方法

2.10 线程工厂

  • 创建线程或线程池时需要指定有意义的线程名,方便排查问题,如下为南网异步线程池线程工厂的应用
    public class WSServiceThreadFactory implements ThreadFactory {private final String namePrefix;private final AtomicInteger nextId = new AtomicInteger(0);public WSServiceThreadFactory(String threadPoolName) {namePrefix = "From WSServiceThreadFactory's " + threadPoolName + "-Worker-";}@Overridepublic Thread newThread(@NotNull Runnable r) {String name = namePrefix + nextId.getAndIncrement();return new Thread(null, r, name, 0);}}

2.11 SimpleDateFormat是线程不安全的

  • 不要定义为static变量,如果定义 为static,必须加锁,或者使用JDK8中的DateTimeFormatterLocalDateTime
  • 测试用例如下
public class TestSimpleDateFormat {private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static void main(String[] args) {Date now = new Date();for (int i = 0; i < 100; i++) {new Thread(() -> {try {String format1 = dateFormat.format(now);Date parse1 = dateFormat.parse(format1);String format2 = dateFormat.format(parse1);// 理论上format1和format2是一样的System.out.println(format2.equals(format1));} catch (ParseException e) {e.printStackTrace();}}).start();}}
}

测试输出结果不是都为true,也会出现false。因为在SimpleDateFormat的format方法中,有calendar对象是线程不安全的,多线程调用时被并发修改。

    private StringBuffer format(Date date, StringBuffer toAppendTo,FieldDelegate delegate) {// 这里并发修改calendarcalendar.setTime(date);...}

3. 代码优化

3.1 通用方法复用

例子中是计算两个时间间隔天数的逻辑,这种通用方法需要加到工具类中复用

3.2 操作资源用try-with-resources资源

3.3 SQL 条件字段不要加函数

  • 否则会使索引失效

3.4 代码注释和方法拆分复用

  • 代码注释描述清晰,其他小方法进行拆分,尽可能复用代码使逻辑清晰

3.5 工作中代码复用的样例

项目中对其他平台接口调用的代码结构是这样儿的,它有3层,给大家简单解释一下。

  1. 接口层:调用外部接口的方法
  2. 抽象层:通用的同步、异步调用逻辑
  3. 实现层:外部接口方法的业务实现

我们需要照样子,为新平台的接口对接,再搬一套类似的结构,像下边儿这样

这个结构照抄起来非常的简单,但是当我写到具体接口实现的时候出现问题了:

我们拿同步派车单异常接口来举例子,两个平台都想要这份数据,而且它们的执行逻辑是一样的,我就在想:“如果不写重复的代码,该怎么复用之前的代码把这个接口开发完?”

当然我们复制一下放到新添加的接口实现类中是可以,但是不优雅,而且还是被idea提示代码重复。

我们先看下这个要复用的方法,它在OutProxyServiceImpl里,是私有的。如果我们想复用它,就需要把它抽出来,抽出来放在哪儿又是一个问题。如果不抽出来,把私有方法变成公开的(public)方法,又需要将调用电网管理平台的实现类注入到调用都匀局的实现类中,业务不相关的bean注入,这样也不好。

public class OutProxyServiceImpl extends AbstractOutProxyService {...private ShipmentErrorDto initShipmentErrorDto(BuException exception) {ShipmentErrorDto errorDto = new ShipmentErrorDto();BuShipment shipment = shipmentService.getById(exception.getShipmentId());// 派车单号errorDto.setShipmentPlanCode(shipment.getCode());// 车牌号errorDto.setPlateNo(exception.getVehicleLp());// 司机姓名errorDto.setDriverName(shipment.getDriverName());// 是否影响到达时间errorDto.setIsAffectArrivalTime(exception.getIsAffectArrivalTime());// 异常等级errorDto.setExceptionLevel(exception.getExceptionLevel());// 异常类别errorDto.setExceptionType(exception.getReason().toString());// 异常内容,取备注errorDto.setExceptionText(exception.getRemark());// 发生时间,格式 yyyy-MM-dd HH:mm:ssSimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");errorDto.setTime(format.format(exception.getEventTime()));return errorDto;}...
}

所以,我想了一个新的代码结构,来放公用的代码,像下边儿这样

还是三层:

  • 接口直接被具体实现类实现,不再关联抽象层
  • 抽象层现在被大家公用,其中放的就是这些同步、异步调用方法和需要被复用的业务方法

那么这样,我们把上述赋值方法拿到抽象类里,用protected修饰,就可以复用它了

虽然解决了问题,但是我还是觉得它不够好,一是业务代码放在抽象层里,不合适;二是这个代码结构变化比较大,在这个基础上又想了另一种方法,如下

这下也实现了代码复用,而且保持代码结构基本不变,只需在抽象层做一层拓展即可。把所有想要复用的方法都拿出来往里边儿放用protected修饰,非常的方便。


“That’s all”

我所理解的代码整洁之道(一)相关推荐

  1. 2015年第11本:代码整洁之道Clean Code

    前一段时间一直在看英文小说,在读到<Before I fall>这本书时,读了40%多实在看不下去了,受不了美国人啰啰嗦嗦的写作风格,还是读IT专业书吧. 从5月9日开始看<代码整洁 ...

  2. 《代码整洁之道:程序员的职业素养》一一1.5 参考文献

    本节书摘来自异步社区出版社<代码整洁之道:程序员的职业素养>一书中的第1章,第1.5节,作者:[美]Robert C. Martin(罗伯特 C. 马丁),更多章节内容可以访问云栖社区&q ...

  3. 《代码整洁之道 Clean Architecture》-读书笔记

    大家好,我是烤鸭: 关于<代码整洁之道>,记录一下读书笔记. 代码整洁之道 第一章 整洁代码 整洁代码的艺术 第二章 有意义的命名 避免误导 有意义的区分 使用读得出来和可搜索的名字 避免 ...

  4. [读书笔记] 代码整洁之道

    书的示例是Java语言编写的,虽说不会影响阅读,但是后面几章讲应用这套方法论的时候,大篇幅的Java代码分析还是挺难受的,而且连java测试框架Junit都要细讲,对于非Java系的开发者来说,一些内 ...

  5. 《代码整洁之道》—第1章1.1节要有代码

    本节书摘来自异步社区<代码整洁之道>一书中的第1章1.1节要有代码,作者[美]Robert C. Martin,更多章节内容可以访问云栖社区"异步社区"公众号查看. 第 ...

  6. 【苦练基本功】代码整洁之道 pt1(第1章-第3章)

    代码整洁之道 pt1(第1章-第3章) 1 整洁代码 1.1 要有代码 1.2 糟糕的代码 1.3 混乱的代价 1.3.1 什么是整洁代码? 2 有意义的命名 2.1 名副其实 2.2 避免误导 2. ...

  7. 【苦练基本功】代码整洁之道 pt4(第10章-第12章)

    代码整洁之道 pt4(第10章-第12章) 10 类 10.1 类的组织 10.2 类应该短小 10.2.1 单一权责原则 10.2.2 内聚 10.2.3 保持内聚性就会得到许多短小的类 10.3 ...

  8. 【苦练基本功】代码整洁之道 pt3(第7章-第9章)

    代码整洁之道 pt3(第7章-第9章) 7 错误处理 7.1 使用异常而非返回码 7.2 先写try-catch-finally 7.3 使用未检异常 7.4 给出异常发生的环境说明 7.5 依调用者 ...

  9. 【苦练基本功】代码整洁之道 pt2(第4章-第6章)

    代码整洁之道 pt2(第4章-第6章) 4 注释 4.1 注释不能美化糟糕的代码 4.2 用代码来阐述 4.3 好注释 4.3.1 法律信息 4.3.2 提供信息的注释 4.3.3 对意图的解释 4. ...

最新文章

  1. java 按钮设置图片_Java中如何设置带图片按钮的大小
  2. 【网络流】人员雇佣(luogu 1791)
  3. python saltstack web_saltstack学习-8:web管理页面(halite)
  4. 【STM32】HAL库 STM32CubeMX教程十四---SPI
  5. Android-Universal-Image-Loader的工具类封装
  6. 适配器模式(Adapter模式)详解
  7. Visual Basic 基础知识
  8. python主页面_使用Wagtail CMS使用Python检测父页面和子页面...
  9. 2021-11-08 RAID磁盘阵列的分级与结构 总结
  10. mysql创建聚集索引sql_SQL:聚集索引和非聚集索引
  11. 495. Teemo Attacking(提莫攻击)
  12. python毕业设计论文-基于Python的网络爬虫的设计与实现.doc
  13. csv文件转换成xlsx文件方法
  14. 2d游戏中角色动画解决方案
  15. js操作元素相关案例
  16. c语言int型是什么意思,在c语言中,int代表什么意思
  17. Dell服务器组建阵列-Raid(无阵列卡)
  18. 基于yolov5的目标检测和模型训练(Miniconda3+PyTorch+Pycharm+实战项目——装甲板识别)
  19. openmeetings二次开发日志(六) openmeetings4.0.3版本及录制屏幕,文档乱码及中英文问题
  20. android 兼容模式设置,天天安卓模拟器切换极速模式与兼容模式的方法

热门文章

  1. 阿布扎比国家石油公司与IBM合作试行区块链
  2. c语言程序中,整型常量的书写形式不包括_________.,??C语言程序中,整型常量的书写形式不包括_________。????...
  3. VxWorks错误码查找表
  4. color = { 国内新闻: ['#D8B221', '#EFE0A6'], 武汉交通: ['#E25E5B', '#E6B2B1']}
  5. 水池蓄水问题(Trapping Rain Water)
  6. 是时候选择诺基亚Windows Phone手机
  7. 物致DIY:OpenMV4——比OpenMV3更快,更高,更强的图像识别DIY神器来了!
  8. 串口232,485转以太网模块 TCP/IP 串口协议转换模块
  9. 看WIZ110SR如何实现串口转以太网功能
  10. 数据分类分级是什么?分类与分级区别又在哪?