今天面试让一个工作 7 年的候选人写 str to int,写不出来。
昨天在 tg 上看到这么一段聊天吐槽🤔,虽然字符串转化逻辑看上去简单,但可以快速考察候选人编码风格与实战能力。尝试自己实现的过程中,刚好发现 java 自带原生实现。这篇文章将简单的分享,阅读对应源码的一些感受 :)
一、需求说明
需求:输入一个字符串,转换并输出一个整数,e.g. "101" -> 101
二、逻辑整理
java 源码参考:java.lang.Long#parseLong(java.lang.String)
整体逻辑整理如上,虽然并不复杂,但值得注意的是:
1)参数防御检验
方法 parseLong
中包含了 非常小心 的防御校验,例如入参非空判断、字符阿拉伯数字判断、数值越界判断等等,短短约 50 行代码中竟共出现 10 次异常分支!
为什么需要防御式编程?
因为你永远也无法预测用户的行为,例如这个视频🤣🤣:
话说刚接触 java 时,对 NPE 十分“恐惧”,如果负责编写的代码发生 NullPointException,虽然无人指责,却也感觉奇耻大辱(这也可能是静态类型过于安全的“副作用”:在以前编写 python 代码时,我们总是以 100% 的但愿测试覆盖率为荣,但用 java 编写一些工具代码时,能通过编译的代码一般不会出错,单元测试的投入产出比不高,反而导致了代码质量的下滑)
言归正传,所以如何适度进行防御性编程?
总不能在每个函数,对参数进行校验吧!?这个问题困扰了我很久...
在《代码大全》第八章中,给出了“标准答案”:
- 隔离: 外部数据检查并进入内部类后,类的私有方法就可以假定数据都是安全的,例如任何东西被允许进入手术室前,都需要进行严格的消毒处理,因此手术室内的任何东西都可以被认为是安全的。
- 健壮性 vs 正确性: 处理错误的方式要根据应用场景而定,如果你的程序对「正确性」要求极高,例如银行内的大额转账,直接返回错误,比持续的运行下去更加合适。
2)关于越界判断
Accumulating negatively avoids surprises near MAX_VALUE
阅读源码中,最让我困惑的一个点:为什么累加数字时,result
是按负数计算的 ???
1 | // java.lang.Long#parseLong(java.lang.String, int) |
为了搞清楚这个问题,我们先来看包装类 Long 中的最大值和最小值:
1 | public final class Long extends Number implements Comparable<Long> { |
不难发现,因为 0 的存在,最大值和最小值不是对称的,abs(最小值) + 1 = 最大值,所以如果用正整数计算,则无法转化 -2^63
三、关于粗心一些思考
记得读书期间,数学考试总是因为粗心导致不理想的分数,后来看到知乎上的一句话才恍然大悟:“粗心,并不是态度问题,而是能力的缺失”。
写代码也是一样,常常因为粗心导致 npe,逻辑不严谨的漏洞等等,但粗心既然是能力的缺失,只要通过努力,就有办法提升。
例如像做数学题一样,永远不要在细枝末节上偷懒(不跳步),反复检查编写用例。
四、其他
btw,阅读代码的时候,意外😯发现原来 java 中也能一行定义两个变量 0.0
1 | // java |
参考
- https://stackoverflow.com/questions/40167218/why-does-the-integer-paseint-algorithm-calculate-negative-result-finally-in-su
- https://leetcode-cn.com/problems/ba-zi-fu-chuan-zhuan-huan-cheng-zheng-shu-lcof/solution/mian-shi-ti-67-ba-zi-fu-chuan-zhuan-huan-cheng-z-4/
- https://dedao.cn/eBook/lxaVvndNG6D4kgLJ2OKxqVMmE1zXPwVvODwAdjyQeYR75vbaBnr9ol8pZERLg1my
- ..