这是一篇关于过度自信的故事。我以为自己是高级程序员,结果输给了20块钱的键盘。
问题出现
上周,我在重构一个老项目。
代码逻辑很简单:用户输入密码,系统验证,返回结果。
但我遇到了一个诡异的问题:
输入正确的密码,提示”密码错误”。
第一轮排查:代码逻辑
第一反应:我的验证逻辑写错了。
检查了半小时:
- 密码哈希算法正确
- 盐值正确
- 数据库查询正确
- 字符串比较用了equals,没有用==
看起来都没问题。
添加日志,打印输入的密码和数据库的密码:
logger.info("输入: {}", inputPassword);logger.info("数据库: {}", dbPassword);logger.info("是否相等: {}", inputPassword.equals(dbPassword));日志输出:
输入: mypassword123数据库: mypassword123是否相等: false???
明明一样,为什么equals返回false?
第二轮排查:字符编码
怀疑是编码问题,有隐藏字符。
检查字符串长度:
logger.info("输入长度: {}", inputPassword.length());logger.info("数据库长度: {}", dbPassword.length());输出:都是13。
转成byte看:
logger.info("输入bytes: {}", Arrays.toString(inputPassword.getBytes()));logger.info("数据库bytes: {}", Arrays.toString(dbPassword.getBytes()));仔细对比,发现第4个byte不一样:
- 输入:
109, 121, 112, 97, ...(a是97) - 数据库:
109, 121, 112, 64, ...(@是64)
等等,我输入的是mypassword123,第四个字符是a,怎么变成@了?
第三轮排查:键盘问题
我突然意识到:可能是我键盘的问题。
打开记事本,按下”a”键——输出”@”
按下”s”键——输出”#”
按下”d”键——输出”$”
…我的键盘Shift键卡住了。
真相大白
Shift键一直处于按下状态,所以我输入的所有小写字母都变成了对应的上档符号:
- a → @
- s → #
- d → $
- …
我以为我输入的是mypassword123,实际上输入的是myP@$$word!23(虽然键盘映射不完全准确,但确实是乱码)。
自我怀疑的三小时
发现真相后,我坐在椅子上发了三小时呆。
不是因为解决了问题而开心,而是对自己产生深深的怀疑:
为什么花了三天才发现?
错误一:过度相信代码
我默认”代码没问题,问题一定在别的地方”。
但实际上,我应该先验证输入是否真的如我预期。
错误二:复杂化问题
我查了:
- 字符编码(UTF-8、GBK)
- 哈希算法实现
- JVM字符串常量池
- 数据库字符集
就是没查最简单的:键盘是否正常。
错误三:忽视常识
当”a”变成”@“时,我应该立即想到Shift键。
但因为过于专注代码,忽视了物理世界的常识。
同事的反应
我把这个故事讲给同事听。
A:“哈哈哈哈哈”
B:“我也遇到过类似的事,最后发现是键盘布局变成了法语”
C:“排查三天算好的,我有一次查了一周,最后发现是显示器线松了”
D:“这叫’过度调试’,程序员职业病”
经验教训
1. 验证输入
在排查复杂问题前,先确认基础:
- 输入真的是你想象的那样吗?
- 输出真的是你看到的那样吗?
- 最简单的情况排除了吗?
2. 从简单到复杂
排查顺序应该是:
- 硬件/物理层(键盘、网线、显示器)
- 环境层(配置、权限、依赖版本)
- 代码层(逻辑、算法)
我反着来了,所以浪费了时间。
3. 保持常识
不要因为写代码而脱离现实世界。
- 程序不运行?看看电脑有没有开机
- 网页打不开?看看网线插没插
- 输入不对?看看键盘是不是坏了
这些听起来很蠢,但真的会发生。
4. 接受自己会犯蠢
每个程序员都有过”低级错误”的经历:
- 花半天找Bug,发现是少了个分号
- 调三天性能,发现代码没部署
- 查一周内存泄漏,发现是日志打印太多
这不是能力问题,是人就会犯错。
重要的是,从这些经历中学习,建立更系统的排查思路。
后续
我换了个键盘,200块的机械键盘。
不是因为20块的键盘不好用,而是想给自己一个教训:
不要让你的工具成为你的瓶颈。
另外,我在工位贴了一张便利贴:
“遇到问题,先检查最简单的情况。”
以及:
“你没那么聪明,也没那么蠢。”
如果你也有类似经历,欢迎分享。我们一起在低级错误中成长。