ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java分支与循环详解:从if-else到switch,彻底吃透流程控制

Java分支与循环详解:从if-else到switch,彻底吃透流程控制 1. 为什么要单独花一天死磕分支与循环如果你正在学Java或者刚开始刷各种Java基础题我对你唯一的建议就是分支和循环这一块别急着往后赶先停下来把它吃透。我见过太多人HashMap源码能讲得头头是道结果让他手写一个while循环实现字符串反转愣是卡了十分钟。原因很简单基础阶段的分支与循环不只是几个关键字它决定了你之后写任何逻辑的思维路径——是先判断还是先遍历是用状态标记还是提前返回这都是在分支与循环这个阶段养成的习惯。day03这个标题看起来平平无奇但它的知识密度其实非常高。你想想凡是程序里稍微有点实际功能的代码哪一个能离开if判断哪一个能离开for循环包括网上那些高频的Java面试题什么和equals的区别如何跳出多重循环switch能不能用字符串本质上全都在考分支与循环的底层理解。所以这篇博客的目的很明确把分支结构和循环结构彻底拆开揉碎把语法背后的执行逻辑、典型应用场景、以及那些容易让人栽跟头的细节全部讲透。适合准备系统复习Java基础的初学者也适合那些已经工作一阵子、想回头把地基补扎实的朋友。我个人的习惯是学这类基础语法不要只盯着概念背要结合人脑是怎么做决策的来理解。if对应的是人脑的如果……就……for对应的是把一件事重复做N次while对应的是只要条件满足就一直做。你带着这种视角去看代码语法就不再是死记硬背了。2. 分支结构先搞懂if-else的执行顺序再看switch的进化史2.1 if-else的语法细节大括号到底能不能省很多教程会说if语句体只有一行的时候大括号可以省略这句话没错但它给新手埋了一个大坑。严格来说if后面不带大括号时它只能控制紧跟其后的第一条语句。看个例子int score 85; if (score 60) System.out.println(及格了); System.out.println(这句永远都会执行);第二个System.out.println根本不在if的控制范围内。这种代码一旦你后来往里面加语句极容易改错。我给你的建议是无论语句体是几行一律写大括号。不要觉得麻烦这是成本最低的防呆设计。见过太多生产环境的事故根源就是有人优化掉了大括号结果把不该吞进来的语句吞进来了。if-else if-else的执行顺序也是个经典考点。它在执行时会从上往下依次判断条件遇到第一个为true的分支就进入执行完直接跳出整个if-else if结构后面的判断全部跳过。所以条件的排列顺序非常重要你要把命中概率高的分支写在前面这样效率更好同时要警惕条件之间存在包含关系。// 错误示范先判断了范围大的后面的分支永远没机会执行 int age 70; if (age 18) { System.out.println(成年人); } else if (age 60) { System.out.println(老年人); }这段代码里age 18把age 60完全覆盖了老年人永远打印不出来。正确的做法是先把范围小的条件写出来int age 70; if (age 60) { System.out.println(老年人); } else if (age 18) { System.out.println(成年人); } else { System.out.println(未成年人); }2.2 判断条件的正确写法布尔表达式和相等比较if后面跟的是一个boolean表达式这一点务必记死。Java不像C语言那样允许if (1)这种写法if后面必须是true或false。所以写条件时有几个习惯建议趁早养成第一个习惯优先使用变量本身作为条件而不是variable true这种冗余写法。boolean isSuccess checkResult(); if (isSuccess) { // 推荐 // ... } if (isSuccess true) { // 不推荐冗余且啰嗦 // ... }第二个习惯比较两个String的内容一定要用equals不能用。这是Java里最经典的新手坑。比较的是引用地址equals比较的是内容。除非你能保证两个字符串是同一个对象比如字面量直接赋值否则用大概率会得到错误结果。String a new String(hello); String b new String(hello); System.out.println(a b); // false因为是两个不同对象 System.out.println(a.equals(b)); // true因为内容相同第三个习惯浮点数比较不要直接用。因为计算机采用二进制表示浮点数时很多十进制小数是无限循环的如0.1在计算机里其实是一个近似值。你直接比较0.1 0.2 0.3得到的可能是false。正确的做法是定义一个很小的误差范围比如1e-6看两个数的差的绝对值是否小于这个范围double x 0.1 0.2; double y 0.3; double epsilon 1e-6; if (Math.abs(x - y) epsilon) { System.out.println(x和y在允许误差范围内相等); }2.3 switch的演进从传统写法到JDK 14的箭头语法传统的switch语句估计大家都见过switch (dayOfWeek) { case 1: System.out.println(星期一); break; case 2: System.out.println(星期二); break; default: System.out.println(无效的星期); break; }这里最让人头疼的就是break。如果你漏写了break会发生穿透——代码会继续执行下一个case的内容直到遇到break或者整个switch结束。这个特性有时候能用来做技巧性设计比如多个case共享同一段逻辑switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: System.out.println(这个月有31天); break; case 4: case 6: case 9: case 11: System.out.println(这个月有30天); break; case 2: System.out.println(这个月需要单独判断闰年); break; default: System.out.println(非法月份); }但从JDK 14开始Java引入了新的switch表达式箭头语法配就不用写break了。每个case箭头后面跟的是一条分支表达式天然不会穿透而且整体可以作为表达式返回一个值String result switch (dayOfWeek) { case 1 - 星期一; case 2 - 星期二; case 3 - 星期三; case 4, 5 - 周末前奏; default - 未知; }; System.out.println(result);如果箭头后面需要多行逻辑需要用大括号包起来配合yield关键字返回值String result switch (score / 10) { case 9, 10 - 优秀; case 7, 8 - { System.out.println(还要加把劲); yield 良好; } default - 继续努力; };这个新特性我强烈建议你在练习时用起来一方面代码更清爽另一方面面试的时候提一嘴JDK 14之后的switch表达式特性会显得你平时一直在跟进版本迭代。3. 循环三兄弟for、while、do-while到底该怎么选3.1 for循环的完整执行流程拆解for循环是所有开发里出现频率最高的循环结构它的语法结构是for (初始化表达式; 布尔条件判断; 步进表达式) { // 循环体 }很多教科书只告诉你三个部分分别干什么但没告诉你它们的真实执行顺序。我用一个非常直观的方式拆解一遍第一步执行一次初始化表达式比如int i 0。第二步判断布尔条件是否为true。如果为false直接跳过整个循环程序继续往下走。第三步条件为true进入循环体执行里面的代码。第四步循环体执行完毕后执行步进表达式比如i。第五步再次回到第二步继续判断条件。也就是说整个流程是初始化 → 判断 → 执行 → 步进 → 判断 → 执行 → 步进……直到某次判断返回false循环结束。这个执行顺序一定要烂熟于心因为很多复杂的嵌套逻辑、死循环问题都源于对这个顺序理解不到位。举个例子你想计算1到100之间所有整数的和int sum 0; for (int i 1; i 100; i) { sum i; } System.out.println(sum); // 5050这段代码的执行过程就是i 1判断1 100为truesum加1i变成2再判断2 100为truesum加2i变成3……直到i 101判断101 100为false循环终止。注意i最后是101不是100因为步进表达式是在条件判断之前执行完的。这个边界细节在调试循环的时候特别容易踩坑。3.2 while循环条件前置典型的当型循环while循环的语义是当条件满足时不断执行循环体。它和for循环最大的区别在于while循环不知道要循环多少次只知道循环的终止条件。典型的使用场景是读取文件直到文件末尾、用户输入直到特定指令、轮询某个状态直到就绪。while (条件判断) { // 循环体 }while循环必须确保循环体内部的逻辑能够改变条件判断的结果否则就会变成死循环。看一个经典的例子——用while实现不断读取用户输入直到输入q退出Scanner scanner new Scanner(System.in); String input ; while (!input.equals(q)) { System.out.print(请输入指令输入q退出); input scanner.nextLine(); System.out.println(你输入了 input); } System.out.println(程序结束);注意这个循环里如果用户输入的是大写Q条件判断就会一直为true程序退不出去。所以实际项目里我们通常会把输入统一转为小写再判断while (!input.toLowerCase().equals(q)) { // ... }while循环还有一个常见的变体——死循环配合break使用。比如服务器的主循环、游戏的帧循环本质上都是一个while (true)然后在内部通过break或特定标志位退出。这种写法本身没毛病但你要确保退出条件一定是可达的否则程序就真的卡死了。3.3 do-while循环条件后置最少执行一次do-while和while长得几乎一样唯一区别是do-while先执行一次循环体再判断条件。所以无论条件一开始是真是假do-while的循环体至少会执行一次。do { // 循环体 } while (条件判断);什么时候用do-while最简单的判断标准是这个循环体是否至少需要执行一次。最典型的例子是菜单系统的交互逻辑——你至少要先把菜单展示出来让用户选一次然后才判断用户选的是不是退出Scanner scanner new Scanner(System.in); int choice; do { System.out.println( 功能菜单 ); System.out.println(1. 查询余额); System.out.println(2. 转账); System.out.println(3. 退出); System.out.print(请输入选项); choice scanner.nextInt(); switch (choice) { case 1: System.out.println(余额为100元); break; case 2: System.out.println(请输入转账金额); break; case 3: System.out.println(正在退出……); break; default: System.out.println(无效选项请重新输入); } } while (choice ! 3);如果这里用while你就得在循环外面先让用户输入一次逻辑会多出一段重复代码。do-while把先执行一次这个语义直接写进语法里代码更干净。面试的时候如果你能主动讲清楚do-while和while的区别是什么各自典型场景是什么这就是一个加分项。3.4 三种循环如何选择一张表说清楚我做了这么多年开发其实选循环有一套很朴实的经验整理成一张表使用场景推荐循环结构理由明确知道循环次数如数组遍历、累加N次for三个部分集中在一行结构清晰不容易遗漏步进次数不确定只有终止条件如读文件、状态轮询while语义最贴合当……时执行至少需要执行一次如菜单交互、验证输入do-while语法层面天然保证一次执行嵌套遍历二维数组、表格数据外层for 内层for两层各自的索引控制清晰4. 循环控制break、continue、return和标签跳转4.1 break立刻终止当前循环break的作用是立刻跳出当前所在的循环不再执行循环体剩余部分也不再执行步进表达式和条件判断。注意当前所在的循环这几个字——它只能跳出一层循环如果是嵌套循环内层break只跳出内层。for (int i 1; i 10; i) { if (i 5) { break; } System.out.println(i); // 只会打印 1 2 3 4 }这里打印到4就结束了因为当i 5时执行了break整个循环直接终止。这个语法特别适合用在提前终止搜索的场景。比如说你在一个数组里找第一个大于100的数一旦找到了就不必继续遍历了int[] nums {12, 58, 3, 120, 99, 150}; int target -1; for (int num : nums) { if (num 100) { target num; break; // 找到第一个就跳出提高效率 } } System.out.println(找到的第一个大于100的数 target); // 1204.2 continue跳过本次循环的剩余部分continue和break完全不同。continue不是结束循环而是跳过当前这一次迭代的剩余代码直接进入下一次迭代。举个例子打印1到10之间所有奇数for (int i 1; i 10; i) { if (i % 2 0) { continue; // 偶数跳过不执行下面的打印 } System.out.println(i); // 打印的是 1 3 5 7 9 }你可能会问判断一下如果是奇数再打印不也一样吗确实但continue的典型价值在于当循环体内部有大量代码时你可以用continue把非法情况提前过滤掉避免后面每行代码都要套一层if。这就是所谓的卫语句风格的雏形。比如处理一批交易记录遇到用户黑名单的直接跳过不用把后续校验逻辑都包在嵌套if里for (Trade trade : tradeList) { if (blackList.contains(trade.getUserId())) { continue; } // 下面一大段都是正常处理逻辑不用再缩进一层 validate(trade); calculateFee(trade); saveTrade(trade); }4.3 return退出整个方法而不仅仅是循环return的作用和break、continue都不一样。它是退出当前方法连循环外层的代码都不执行了。也就是说如果你在for循环里写了return它会直接结束整个方法的调用方法后面的语句全都不执行。public static boolean containsNegative(int[] nums) { for (int num : nums) { if (num 0) { return true; // 找到一个负数就直接返回结果整个方法结束 } } return false; // 循环跑完了都没找到负数 }这种写法在判断是否存在满足条件的元素时非常好用。它的语义是一旦发现就可以提前宣告结果省去了继续无意义的遍历。不过有一点要提醒如果循环结束后方法还需要做收尾工作比如释放资源、记录日志用return就要考虑收尾代码是否会被跳过。一般情况下有资源需要关闭的场景更推荐break配合标志位或者用try-with-resources。4.4 跳出多重循环的两种主流方案嵌套循环里内层break只能跳出内层这是个非常经典的痛点。比如你想在二维数组里找到第一个等于0的元素的位置然后彻底结束两层循环int[][] matrix { {1, 2, 3}, {4, 0, 6}, {7, 8, 9} };你要是这么写会发现一个问题break只跳出了内层for外层for还会继续跑下一行导致找到目标之后还在无意义地遍历。网上最常见的解法有两种。方案一使用带标签label的break。Java里break后面可以跟一个标签直接跳出标签指定的循环层。标签的写法是在循环语句前面加一个标识符加冒号outer: // 标签命名随意但要符合标识符规范 for (int i 0; i matrix.length; i) { for (int j 0; j matrix[i].length; j) { if (matrix[i][j] 0) { System.out.println(找到0的位置( i , j )); break outer; // 跳出外层循环 } } } System.out.println(循环结束);break outer会直接跳出outer标签所在的那一层循环两层的遍历都停下来。这个语法平时用到的概率不算特别高但在多重循环里确实是最直白的手段面试偶尔会问如何跳出多重循环你能说出标签跳转就是个加分项。方案二使用标志位flag。定义一个boolean变量内层改变标志位外层每次循环判断标志位为true就终止boolean found false; for (int i 0; i matrix.length !found; i) { for (int j 0; j matrix[i].length; j) { if (matrix[i][j] 0) { System.out.println(找到0的位置( i , j )); found true; // 改状态 break; // 跳出内层 } } }这个方案的优点是逻辑直觉化任何版本的Java都支持也更容易读懂缺点是代码比标签法啰嗦一点。我个人在实际开发中用标志位更多因为团队协作时标签跳转总有人看不明白。5. 实战演练用分支与循环解决经典编程题5.1 打印九九乘法表嵌套循环的入门必修课九九乘法表是练习嵌套for循环的经典题目它的核心是外层循环控制行数内层循环控制每行的列数。注意内层循环的上界是和外层循环变量挂钩的——第i行只需要打印1到i列。for (int i 1; i 9; i) { for (int j 1; j i; j) { System.out.print(j × i (i * j) \t); } System.out.println(); // 换行 }我们来分析一下这个代码的执行过程。外层i 1时内层j从1到1输出1×11然后换行外层i 2时内层j从1到2输出1×22 2×24以此类推。最终输出一个左下三角形的乘法表。这里有个关键点内层循环的j i而不是j 9这是实现三角形效果的核心条件。如果想打印右上三角版本只需要调整内层循环的起始值for (int i 1; i 9; i) { // 先打印前导空格占位让右上三角对齐 for (int k 1; k i; k) { System.out.print(\t); } for (int j i; j 9; j) { System.out.print(i × j (i * j) \t); } System.out.println(); }这道题虽然简单但非常能锻炼你控制循环边界的能力。很多初学者写嵌套循环时容易把内外层变量搞混我建议你手写一遍执行过程把每一层的变量值标出来你会比只看不写的人理解深得多。5.2 判断质数标志位和提前终止的完美结合判断一个数是不是质数是分支与循环综合应用的经典题目。质数的定义是大于1的自然数除了1和它本身以外不能被其他自然数整除。public static boolean isPrime(int num) { if (num 2) { return false; } for (int i 2; i * i num; i) { if (num % i 0) { return false; // 能被整除不是质数提前返回 } } return true; }这里有两个细节值得说明。第一个细节是循环上限为什么是i * i num。如果num有一个大于sqrt(num)的因子那么它必然对应一个小于sqrt(num)的因子比如16的因子是4和418的因子是3和6。所以遍历到sqrt(num)就足够了。用i * i num而不是i Math.sqrt(num)的目的是避免每次循环都计算一次平方根提高一点效率。面试时如果问你质数判断怎么优化这个就是最常见的优化点。第二个细节是用return提前退出。一旦发现能被整除这个数就不是质数后面没必要继续试除了。这种尽早返回的思想在算法题里非常实用。5.3 求最大公约数辗转相除法里的循环思路最大公约数GCD的经典算法是辗转相除法思路如下两个正整数a和b假设a b用a除以b取余数r如果r为0则b就是最大公约数否则让a bb r继续循环。用while循环实现非常贴合这个算法的不断重复直到条件满足的特点public static int gcd(int a, int b) { while (b ! 0) { int temp b; b a % b; a temp; } return a; }手动推演一下假设a 18b 12。第一次循环temp 12b 18 % 12 6a 12第二次循环temp 6b 12 % 6 0a 6循环结束返回a 6。答案正确。这道题的价值在于它让你体会到循环的迭代感——每一次循环都在用上一次的结果作为输入状态在不断向前推进直到达到终止条件。这和for循环那种计数器式的思维是互补的。如果你只会写for循环遇到这类状态迭代问题就会觉得别扭while循环就是为这类场景准备的。5.4 水仙花数数字拆分与位运算的综合练习水仙花数是指一个三位数其各位数字的立方和等于该数本身比如153 1^3 5^3 3^3。这个题可以很好地练习数字的拆分和条件判断for (int i 100; i 999; i) { int hundreds i / 100; // 百位 int tens i / 10 % 10; // 十位 int ones i % 10; // 个位 int sumOfCubes hundreds * hundreds * hundreds tens * tens * tens ones * ones * ones; if (sumOfCubes i) { System.out.println(i 是水仙花数); } }关键点在于用整除和取模来拆位i / 100得到百位i / 10 % 10得到十位i % 10得到个位。其中i / 10 % 10的顺序是先整除10去掉个位再取模10取出现在的个位也就是原来的十位。这种拆位技巧在数字类题里出现频率极高蓝桥杯之类的比赛里也经常有类似题目。6. 常见踩坑记录这些代码为啥和你想象的不一样6.1 死循环的产生原因与调试思路死循环是循环这块最常见的报错形式了——程序跑起来之后一直不结束CPU占用率拉满。产生死循环的根源通常是循环条件始终为true且循环体内没有任何机制可以改变这个条件。有个经典的例子是int i 1; while (i 10) { System.out.println(i); // 忘了写 i }这里因为没有ii永远是1条件1 10永远为真循环永远跑不完。初学者特别容易在while循环里忘了更新循环变量因为for循环的步进表达式写在语法里不容易漏但while需要手动写。还有一种容易忽略的死循环是for循环的步进表达式和continue冲突for (int i 1; i 5; i) { if (i 3) { continue; } System.out.println(i); }这个例子没问题因为continue之后步进表达式i依然会执行。但如果你把continue放在while循环的末尾之前int i 1; while (i 5) { if (i 3) { continue; // i没有自增下一次还是3死循环 } System.out.println(i); i; }当i 3时执行continue跳过了后面的i下一次循环i还是3又执行continue无限循环。这是while循环里非常隐蔽的坑。解决方式是把i放在continue之前或者改用for循环。调试死循环的手段我建议你优先用在关键位置打印变量值或者直接用IDE的断点调试查看变量的实时变化。IDEA里设置断点然后单步执行Step Over你会清楚地看到程序在哪一步开始重复执行、哪个变量的值一直没变问题通常一眼就能找到。6.2 switch穿透到底是坑还是技巧switch的穿透特性在前面提过我在这里专门讲一下它的双面性。先看一个反面案例int day 2; switch (day) { case 1: System.out.println(周一); case 2: System.out.println(周二); case 3: System.out.println(周三); default: System.out.println(其他); }运行结果是什么不是只打印周二而是从case 2开始一路打印周二周三其他。因为你忘了写break代码穿透了。这个特性非常考验细心程度特别是当你往已有的switch结构里添加新case时稍不注意就会漏掉break。但从另一个角度说穿透特性可以用来做多个case共享逻辑的设计比如前面提到的月份天数判断、case 1到case 3合并输出一季度int month 2; switch (month) { case 1: case 2: case 3: System.out.println(第一季度); break; case 4: case 5: case 6: System.out.println(第二季度); break; default: System.out.println(其他季度); }这里month 2时会从case 1穿透进来经过case 2到case 3的代码块才执行然后遇到break跳出。效果就是1、2、3月都打印第一季度。这种写法是安全的也是穿透特性的正面应用。但如果你用JDK 14之后的箭头语法就不存在这个顾虑了case 1, 2, 3 -直接表达同样的含义。6.3 循环内创建大量对象导致的性能隐患在循环体里new对象这件事本身不违规但你要是循环次数很大频繁创建对象就会带来明显的性能开销和内存压力。一个典型的场景是在循环里拼接字符串使用String String的方式String result ; for (int i 0; i 10000; i) { result result 第 i 条数据\n; }Java里String是不可变对象每次用拼接都会创建一个新的String对象把旧内容复制过去。循环1万次就创建了近万个中间字符串对象既慢又浪费内存。正确做法是用StringBuilderStringBuilder sb new StringBuilder(); for (int i 0; i 10000; i) { sb.append(第).append(i).append(条数据\n); } String result sb.toString();这个点虽然严格来说属于循环体写法优化但它发生在循环这个语境下非常值得在这里提醒。以后你刷题、做项目凡是循环里涉及字符串拼接第一反应用StringBuilder就对了。一个更隐蔽的问题是循环里重复执行相同的初始化操作。比如你有一个List要反复查找某个元素每次都在循环里调用Collections.sort来排序后再查找而排序其实只需要做一次。这种逻辑问题不是语法问题而是循环体里做了什么的设计问题。写循环的时候养成一个习惯不变的步骤提到循环外面只有真正随迭代变化的才留在循环体里。6.4 边界值错误为什么循环常常多一次或少一次边界条件写错是循环里最常见的bug来源。最典型的问题是和的区分。我见过非常多的错误是想要遍历数组所有元素但条件写成了i arr.length - 1导致最后一个元素永远访问不到以及反过来的想遍历到最后一个元素的前一个位置却用了i arr.length - 1导致越界。这里有一个一劳永逸的检查方法拿边界值代入验算。假设数组长度是5条件i 5i的取值范围是0到4正好覆盖五个元素的下标条件i 5遍历到i 5时访问arr[5]下标越界了。再看i arr.length - 1i只能取0到3第4个元素arr[4]被漏掉了。只要你写的每个循环都习惯性拿首尾两个边界值代入走一遍这类错误基本能自己发现。还有一个常见边界问题是从1开始还是从0开始。Java的数组索引从0开始但很多业务计数从1开始两者混在一起就特别容易错。我的经验是循环变量明确注释它的含义比如for (int row 0; row rows; row)注释row是行索引或者直接使用增强for循环减少下标操作int[] arr {1, 2, 3, 4, 5}; for (int num : arr) { System.out.println(num); }增强for循环隐藏了索引变量从根本上规避了数组越界的风险但代价是你拿不到当前下标。如果你同时需要值和下标传统的for还是更合适如果只需要值优先用增强for代码可读性也更高。7. 分支与循环在真实项目里的常见组合模式7.1 遍历集合加条件筛选最常用的循环if模式真实项目里循环和分支极少单独出现它们总是组合在一起。最典型的组合就是遍历一个集合用if筛选出符合条件的元素再进行后续处理。// 从订单列表中筛选出金额大于100且状态为已支付的订单 ListOrder paidOrders new ArrayList(); for (Order order : orderList) { if (order.getAmount() 100 PAID.equals(order.getStatus())) { paidOrders.add(order); } }这段代码的逻辑就是遍历 判断 收集。别看它简单它几乎是所有数据处理类需求的骨架。后面你学Stream流式编程时会发现它就是把这个模式封装成filter和collect的组合但底层思维还是这里的分支与循环。实际工作里这种模式经常要配合边界情况处理比如集合为null的情况。老练的开发者会在遍历前加一个判空ListOrder paidOrders new ArrayList(); if (orderList ! null) { for (Order order : orderList) { // ... } }7.2 循环标记法用标志位控制多阶段的流程还有一种在项目里很常见的模式是用标志位控制循环是否继续。这种模式适合某个流程需要持续运行直到满足特定条件才停止的场景。boolean isRunning true; while (isRunning) { String command getNextCommand(); switch (command) { case start: startEngine(); break; case stop: isRunning false; // 改变标志位退出循环 break; default: System.out.println(未知命令); } }这个模式的核心是while的条件是标志位而标志位只会在内部某些分支中被修改。这样的代码读起来非常清晰——isRunning就像一条主线把整个流程串起来。比直接用while (true)配合break的写法更容易理解整体退出逻辑。7.3 双重循环处理二维结构表和矩阵的遍历套路二维数组、矩阵、表格数据处理它们的基本套路就是双重循环。外层循环遍历行内层循环遍历列matrix[i][j]可以访问到每个单元格。这个模式配合是否满足某条件就跳过统计满足条件的单元格数量就形成了很多算法题的雏形。int[][] scores { {90, 85, 78}, {88, 92, 80}, {70, 75, 60} }; // 统计所有成绩中大于等于85分的数量 int count 0; for (int i 0; i scores.length; i) { for (int j 0; j scores[i].length; j) { if (scores[i][j] 85) { count; } } } System.out.println(85分及以上的成绩个数 count);写双重循环最重要的心法是清晰区分外层变量和内层变量并且明确每一层的含义。比如这里外层i是行索引内层j是列索引。一旦混淆很容易写出行列颠倒的bug而这种bug在代码审查里极难发现因为语法完全合法。8. 一些可以对别人说的我在实际敲码时的体会刷day03这种基础章节很容易让人产生我全都会了的错觉但实际上基础知识的真正考验在于能不能闭着眼写对。我在实际教学过程里经常做的一件事是让学习者把本子合上手写一个1到100之间所有偶数的和大部分人会写得磕磕绊绊因为平时都在抄代码而不是自己构造逻辑。所以我自己带人的时候非常强调一件事每个分支和循环的练习题先用中文把逻辑写出来再翻译成代码。比如判断质数这道题中文逻辑是先排除小于2的数然后从2开始试除一直试到这个数的平方根如果存在一个数能整除它就不是质数。把这段话变成代码就是if for if return的组合。这个先写伪代码、再写代码的习惯能帮你把分支与循环从语法知识变成解决问题的工具也就是所谓的编程思维。还需要提醒的是分支与循环的练习过程中有个心理陷阱——不要一上来就用复杂写法。我见过有人学了两天就尝试用嵌套三元运算符和多重循环写一个看起来很炫技的题目结果自己都看不懂调试了一晚上。基础阶段的目标不是炫技是稳固理解。每道题先用最直白的方式写出来跑通后再想这里能不能优化这才是可持续的进阶路径。另外想多说一句关于面试的事情。很多人问Java面试题里怎么考分支循环其实考的不是语法本身而是你分支和循环写得地不地道。比如for循环遍历数组时你是用i arr.length还是i arr.length - 1判断字符串相等时用没用equals嵌套循环里跳出的方案你都掌握了几种质数判断的优化点能不能说出来。这些细节都能反映一个开发者的基本功扎不扎实。所以今天的这些内容不只是为了让你会做题更是为了让你在将来的代码评审和面试里每一个分支、每一个循环都写得让人挑不出毛病。
返回列表