记得我刚入行那年,为了一个用户输入验证的功能,在办公室熬到凌晨三点。屏幕上那个该死的循环就像鬼打墙一样,不停地转啊转,我盯着控制台输出的错误日志,恨不得把显示器给砸了。那是我第一次真正理解,为什么导师总说“循环虽简单,用错能要命”。

今天我想聊聊while和do while这两个看似基础,却让无数程序员栽过跟头的循环结构。话说回来,我可能对while有点偏爱,这大概源于早期被do while坑惨的经历。
先检查再打扫的管家
如果把while循环比作什么,我觉得它像个谨慎的管家——每次打扫房间前,都要先探头看看里面脏不脏。语法上,while就是这样:“先判断条件,再决定执不执行”。
我的团队去年重构过一个数据清洗模块,里面就有个典型的while用例:
while (dataReader.hasNext()) {
DataRecord record = dataReader.next();
processRecord(record);
}
这种“先检查后使用”的模式在处理不确定数量的数据时特别安全。有趣的是,我们最初这里用的是do while,结果在空数据集上直接抛了NullPointerException——那可是个线上故障,害得我们半夜被叫起来回滚版本。
话说回来,while循环最让人头疼的就是边界条件。我有个血泪教训:2016年在电商项目做购物车数量校验时,我写了个:
int itemCount = 0;
while (itemCount < maxAllowed) {
// 处理商品
itemCount++;
}
看起来没问题对吧?但测试时发现当maxAllowed为0时,循环居然执行了一次!调试了半天才发现是别处的代码在循环前误改了itemCount。这事教会我:while循环的边界就像走钢丝,稍有不慎就会掉下去。
先递传单再说话的推销员
do while呢?我常开玩笑说它像个热情的推销员——不管你要不要,先把传单塞你手里再说。它的逻辑是“先执行一次,再判断要不要继续”。
坦白说,do while在某些场景下确实很贴心。比如我们做过的命令行工具,要求用户至少输入一次密码:
do {
System.out.print("请输入密码:");
password = readInput();
} while (!isValidPassword(password));
这种“保证至少执行一次”的特性,让代码读起来特别自然。我的意思是,既然用户总得输入密码,为什么非要先用while检查呢?
但do while的“热情”有时会过头。2018年我们系统出现过一次内存泄漏,追查下来居然是个do while循环惹的祸:
do {
byte[] buffer = readChunk();
process(buffer);
} while (buffer != null);
理论上,当readChunk()返回null时循环应该结束。但实际上,由于网络延迟,第一次读取就可能超时抛出异常,而do while已经分配了内存资源。结果就是在高并发时,这个“至少执行一次”的设计成了资源黑洞。
什么时候该用谁?
凭我这些年的经验,选择循环其实是个权衡题。当你不确定要执行多少次,但很清楚“可能一次都不执行”时,while是更安全的选择。
另一方面,当你确定代码至少需要执行一次,do while往往更直观。比如我们做过的文件解析器:
do {
FileHeader header = readHeader();
// 解析文件必须从头开始,不可能跳过header
} while (!header.isValid());
测试数据显示,在同样逻辑下,do while比while平均少了约2ms的判断开销——虽然微不足道,但在高频交易场景下,这种差异会被放大。
等等,我可能说得太绝对了。实际上,现代编译器优化得很厉害,性能差异几乎可以忽略。更重要的考量其实是代码的可读性和维护成本。
那些年我们踩过的坑
我永远记得那个周末晚上,运维打电话说服务器CPU飙到100%。查看日志发现一个while循环在空转:
while (taskQueue.isProcessing()) {
// 等待任务完成
}
问题出在:某个任务异常后没有更新processing状态,于是这个循环就永远停不下来了。如果当初写成带超时机制的do while,至少还能自动恢复。
还有一次代码审查,我发现新手同事写了这样的代码:
do {
result = database.query(sql);
// 处理结果
} while (result.next());
看起来没问题?但在数据库连接异常时,第一次执行就会崩溃。我们最后改成了:
if (database.isConnected()) {
do {
result = database.query(sql);
// 处理结果
} while (result.next());
}
所以说啊,循环选择不只是技术问题,更是对异常情况的预判。
我的偏见与反思
坦白说,我80%的时间都在用while循环。这种偏好可能源于早期教训——被do while的“无条件执行一次”特性坑过太多次。但平心而论,这确实是我的偏见。
最近在做代码复盘时,我发现团队里do while的使用率只有while的十分之一。这未必是好事,因为有些场景下,do while其实能让代码更简洁。
比如上个月重构的消息队列消费者:
do {
Message msg = queue.poll();
if (msg != null) process(msg);
} while (!queue.isEmpty());
比等价的while版本少了重复代码,读起来也顺畅很多。
循环背后的工程哲学
说到底,while和do while的选择反映了一种工程思维:你要多谨慎?愿意为安全性牺牲多少简洁性?
在我的职业生涯中,越来越觉得编程就像生活——没有绝对正确的选择,只有更适合场景的权衡。while教会我审慎,do while让我看到直接行动的价值。
也许有一天,我会改变对do while的保守态度。但至少现在,我还是要说:当你犹豫不决时,选while通常更安全。这不是技术上的最优解,却是工程实践中的稳妥之选。
回头看看这些年写的代码,循环就像老朋友,既帮助我构建了复杂的系统,也用各种bug让我成长。下次写循环时,不妨多想三秒钟:这个循环真的需要执行吗?如果必须执行,它最少应该执行几次?
相信我,这点思考能帮你避开很多深夜调试的煎熬。毕竟,凌晨三点的办公室咖啡,真的不好喝。


评论