Code Review 是一场苦涩但有意思的修行。

(一)改掉这些坏习惯,还怕写不出健壮的代码?

(二)改掉这些坏习惯,还怕写不出优雅的代码?

(三)改掉这些坏习惯,还怕写不出优雅的代码?

书接上篇,本次继续探讨一下,该如何写出健壮的代码?

1. 编码时:看似顺眼,实则不然。 

举个栗子:

String amount = request.getParameter("amount");
// 校验金额小数点后最多两位小数
BigDecimal a = new BigDecimal(amount);
if (a.doubleValue() * 100 - Math.floor(a.doubleValue() * 100) != 0) {System.out.println("交易金额错误");// do something ... ...
}

摘一段跑在生产环境上的代码,代码咋一看没啥问题,主要功能是获取请求参数;然后完成数据校验。

看似很顺眼,但是你细品,就会发现其中之奥秘,下面一起在本地跑跑代码,来分析一下到底会存在什么问题?

问题一:坑死人的 NPE

输入:null(当 amount 输入为空时)

输出:

Exception in thread "main" java.lang.NullPointerExceptionat java.math.BigDecimal.<init>(BigDecimal.java:806)at PayController.main(PayController.java:300)

分析:

根据上面异常信息,见 BigDecimal 的源码第 806 行,如下图所示,很显然 BigDecimal 构造不会判断传入的 val 是否为空,所以会出现空指针异常。

目前没有出现问题,那只能算庆幸,不过终究是个定时炸弹。切记调用 BigDecimal 的构造时,请勿传入 null 值。

心声:

身边老码农真真的排查了好长时间,问题场景与此类似,直接阻断了程序后续的流程。

问题二:同样是传入数字,结果咋就匪夷所思。

输入:

6666.66(当 amount 输入为 6666.66)

当 amount 输入为 6666.66 时,amount 的值校验通过。真的是看到的这个样子吗?换个数试试呗。

输入:

8888.88(当 amount 输入为 8888.88)

输出:

交易金额错误

分析:

容我拆解一下代码,当 amount 传入为 8888.88 时:

double d1 = a.doubleValue() * 100;
double d2 = Math.floor(a.doubleValue() * 100);
System.out.println(d1); // 输出:888887.9999999999
System.out.println(d2); // 输出:888887.0
System.out.println(d1 - d2); // 输出:0.9999999998835847

很显然, d1 - d2 的值 != 0,那么如下表达式的值则满足,会输出交易金额错误。

为什么呢?归根揭底是 double 运算时精度丢失而导致程序处理出错,虽然在 Java 中提倡用 BigDecimal 进行四则运算,但是上面的校验实现,貌似跟 BigDecimal 没有啥关系,到底该怎么解决呢?

无脑实现方式:

if (amount.contains(".") && amount.substring(amount.indexOf(".") + 1).length() > 2) {System.out.println("校验失败 2");// do something ... ...
}

如上面代码段所示,直接判断传入的 amount 字符串小数点后面的位数就可以啦。

当然,仁者见仁智者见智,实现方式有很多,不去多深究。

2. 编码时:时间转换也作祟 

举个栗子:

public static long convertDaysToMilliseconds(int days) {return 1000 * 3600 * 24 * days;
}

分析:

1000 * 3600 * 24 * days 结果默认为 int 类型,最大值为 2147483647,如果超过 int 范围,则会出现截断,程序不会出错,但是结果却匪夷所思。

例如:

当 days 输入为 30 时,程序输出:-1702967296。

改进方式一:

改进方式二:

再举个栗子:

public static Date getDate(int seconds) {return new Date(seconds * 1000);
}

分析:

当 seconds * 1000 值为 int 类型,当超过 int 最大值为2147483647 时,程序不会出错,但是结果却匪夷所思。

改进方式:

分享一下心声:

  • 禁止使用 double 直接参与金额运算,会出现意想不到的结果。

浮点数采用“尾数+阶码”的编码方式,类似于科学计数法的“有效数字+指数”的表示方式。 二进制无法精确表示大部分的十进制小数。

—— 请自行科普,留作业。

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

BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。

如:BigDecimal g = new BigDecimal(0.1f); 实际的存储值为:0.10000000149

优先推荐入参为 String 的构造方法,或使用 BigDecimal 的 valueOf 方法,此方法内部其实执行了 Double 的 toString,而 Double 的 toString 按 double 的实际能表达的精度对尾数进行了截断。

—— 阿里开发手册

  • 那些看似顺眼的代码,或者线上跑着的代码,未必就没问题,只是没有走到异常分支上去,随着时间的推移,定时炸弹迟早会爆,定期审查代码,以及充分的测试是非常的必要。

3. 寄语写最后 

我等采石之人当心怀大教堂之愿景——《程序员修炼之道》。

好了,代码修炼的系列分享,本次就谈到这里,一起聊技术、谈业务、喷架构,少走弯路,不踩大坑。会持续输出原创精彩分享,敬请期待!

推荐阅读:

(一)改掉这些坏习惯,还怕写不出健壮的代码?

(二)改掉这些坏习惯,还怕写不出优雅的代码?

(三)改掉这些坏习惯,还怕写不出优雅的代码?

(四)改掉这些坏习惯,还怕写不出健壮的代码?相关推荐

  1. java十进制小数转化为二进制小数代码 乘二取整法_(四)改掉这些坏习惯,还怕写不出健壮的代码?...

    Code Review 是一场苦涩但有意思的修行. (一)改掉这些坏习惯,还怕写不出健壮的代码? (二)改掉这些坏习惯,还怕写不出优雅的代码? (三)改掉这些坏习惯,还怕写不出优雅的代码? 书接上篇, ...

  2. (一)改掉这些坏习惯,还怕写不出健壮的代码?

    Code Review 是一场苦涩但有意思的修行. 近期对团队负责的项目,进行了一次 Code Review,代码评审过程中遇到的那些编码坏习惯,笑的合不拢嘴.不过,评审中很多代码编写问题,以往都多次 ...

  3. (六)改掉这些坏习惯,还怕写不出精简的代码?

    鲁迅说:嬉笑怒骂里充满了无奈和妥协. 小猿说:先生说的不对,在程序员的世界里,编写代码处处充满了无奈和妥协. (一)改掉这些坏习惯,还怕写不出健壮的代码? (二)改掉这些坏习惯,还怕写不出优雅的代码? ...

  4. (二)改掉这些坏习惯,还怕写不出优雅的代码?

    Code Review 是一场苦涩但有意思的修行. 上期分享,通过示例剖析编码中一些经常触犯的性能点,以及编码时常犯的一些小毛病,来告诉新手程序员如何写出健壮的代码. 咱们书接上篇,本次一起来探讨一下 ...

  5. (五)改掉这些坏习惯,还怕写不出精简的代码?

    鲁迅说:嬉笑怒骂里充满了无奈和妥协. 小猿说:先生说的不对,在程序员的世界里,编写代码处处充满了无奈和妥协. Code Review 是一场苦涩但有意思的修行. (一)改掉这些坏习惯,还怕写不出健壮的 ...

  6. (三)改掉这些坏习惯,还怕写不出优雅的代码?

    Code Review 是一场苦涩但有意思的修行. 书接上篇,本次一起继续探讨一下,该如何写出优雅的代码? 1. 编码时:搞的复杂并不好  坏习惯一:多余的 if/else. 反例: 类似上面这种写法 ...

  7. 每月改掉一个坏习惯,遇见更好的自己

    -1- 有人说,人生不过是无数习惯的总和. 查尔斯·杜希格在<习惯的力量>中写道:「人每天的活动中,有超过40%是习惯的产物,而不是自己主动的决定.虽然每个习惯的影响相对来说比较小,但是随 ...

  8. 如何写出健壮的代码?

    简介:关于代码的健壮性,其重要性不言而喻.那么如何才能写出健壮的代码?阿里文娱技术专家长统将从防御式编程.如何正确使用异常和 DRY 原则等三个方面,并结合代码实例,分享自己的看法心得,希望对同学们有 ...

  9. 改掉这些坏习惯让你的学习效率升高

    IT产业的发展速度很快,所以必须不断地学习新的知识.如果您希望在web前端领域取得更好的发展,可以看这些的小建议 一.低效率的学习坏习惯 1.被动重复阅读一般初学者在学习web前端开发时,会倾向于购买 ...

最新文章

  1. 【六一儿童节】回忆一下“孩子们的游戏”!(码农版)
  2. Ubuntu下建立Pycharm快捷方式
  3. 关闭 Visual Studio 2013 的 Browser Link 功能
  4. An example of parsing xml file using Scala
  5. 自动化测试===adb 解锁手机的思路
  6. c语言oj1124,程序设计入门——C语言 第2周编程练习 1时间换算(5分)
  7. 二手房六大产权问题最关键
  8. PHP之数组函数(2)
  9. 新浪微博api接口java_Java调用新浪微博API【转】
  10. 传奇服务端GOM引擎和GEE引擎区别在哪里?
  11. 小程序 40163_微信小程序请求openid错误码40163
  12. nmos导通流向_MOS管知识大集
  13. Composition
  14. Oracle 11gR2光钎链路切换crs服务发生crash
  15. 是谁在觊觎娱乐圈站点?揭秘神秘黑客组织-黑界
  16. PCAN-USB FD选型使用比较
  17. d-ary heaps 多叉树堆排序C++实现
  18. js 页面跳转两种方式(原页面跳转,打开新标签页)
  19. 小学计算机画线反思,会画画的线活动反思
  20. 最有元宇宙“面相“的Discord, 及腾讯/阿里/网易/百度/字跳元宇宙可行性路径分析

热门文章

  1. 【百问网7天物联网智能家居】训练营学习笔记(七)
  2. 现代微积分学的公理观
  3. 多种JS刷新页面代码!
  4. geoip2配置及使用
  5. 很不错的免费杀毒软件
  6. Java面试题:GC 是什么? 为什么要有GC?
  7. as.net core 5.0 Configuration读取consul的kv存储
  8. matlab set函数
  9. MXNet:基础和入门
  10. spring security+thymeleaf登录失败以及验证码错误提示