当前位置: 首页 > news >正文

第三章 流程控制

主要内容

  1. if选择结构
  2. switch选择结构
  3. for循环结构
  4. while循环结构
  5. do-while循环结构
  6. break和continue关键字
  7. 嵌套循环
  8. 带标签的break和continue
  9. 方法的使用
  10. 方法重载
  11. 递归(recursion)

学习目标

知识点要求
if选择结构掌握
switch选择结构掌握
for循环结构掌握
while循环结构掌握
do-while循环结构掌握
breakcontinue关键字掌握
嵌套循环掌握
带标签的breakcontinue了解
方法的使用掌握
方法重载掌握
递归 (recursion)掌握

1. 选择结构

Java程序的执行流程主要包含三种基本结构:顺序执行、选择执行和循环执行。
在这里插入图片描述

1.1 if选择结构

1.1.1 if单选结构

在这里插入图片描述
【语法结构】

if (条件表达式) {// 条件为true时执行的代码块
}

【工作原理】

  1. 条件表达式计算结果必须是boolean类型(true或false)
  2. 执行流程:
    • 如果条件为true:执行if语句块中的代码,然后继续执行后续语句
    • 如果条件为false:跳过if语句块,直接执行后续语句

【语法规范】

  1. 条件表达式必须用小括号()括起来
  2. 建议始终使用大括号{}包围代码块,即使只有一条语句
  3. 条件表达式后的分号应该在大括号后面,不能直接在条件后面加分号

【使用注意】

  • 可以包含任意多条语句,但建议保持结构简单清晰
  • 条件表达式可以是:
    • 关系运算表达式
    • 逻辑运算表达式
    • boolean类型变量
    • 返回boolean值的方法调用

【示例代码】

/*** @Author: chenxiezhu* @Date: 2025/5/15 20:09* @Version: v1.0.0* @Description: TODO**/
public class IfStatementExample {public static void main(String[] args) {// 示例1:判断是否及格int score = 85;if (score >= 60) {System.out.println("考试及格!");System.out.println("当前分数:" + score);}// 示例2:判断是否是VIPboolean isVIP = true;if (isVIP) {System.out.println("尊敬的VIP用户,欢迎回来!");System.out.println("您可以享受专属优惠!");}}
}

在这里插入图片描述

【随堂练习】

  1. 根据年龄判断是否已经成年,年龄大于18表示成年。
  2. 判断一个数是否在16(包含)到23(包含)之间。

1.1.2 if双选结构

在这里插入图片描述
【语法结构】

if (条件表达式) {// 条件为true时执行的代码块
} else {// 条件为false时执行的代码块
}

【工作原理】

  1. 条件表达式计算结果必须是boolean类型
  2. 执行流程:
    • 如果条件为true:执行if代码块,跳过else代码块
    • 如果条件为false:跳过if代码块,执行else代码块
  3. if-else结构保证两个分支必定执行其中之一

【语法规范】

  1. 条件表达式必须用小括号()括起来
  2. else语句必须紧跟在if语句的结束大括号后面
  3. 建议始终使用大括号{}包围代码块,即使只有一条语句
  4. if和else分支的代码块应保持相同的缩进级别

【使用注意】

  • 每个分支可以包含任意多条语句
  • 可以在分支内再嵌套其他控制结构
  • 程序最终只会执行其中一个分支

【示例代码】

/*** @Author: chenxiezhu* @Date: 2025/5/15 20:12* @Version: v1.0.0* @Description: TODO**/
public class IfElseExample {public static void main(String[] args) {// 示例1:基础判断 - 成年与未成年int age = 16;if (age >= 18) {System.out.println("已成年,可以独立签署合同");} else {System.out.println("未成年,需要监护人陪同");}// 示例2:考试成绩判断int score = 55;if (score >= 60) {System.out.println("考试及格!");System.out.println("当前分数:" + score);} else {System.out.println("考试不及格,需要补考");System.out.println("当前分数:" + score + ",距离及格还差" + (60 - score) + "分");}// 示例3:奇偶数判断int number = 7;if (number % 2 == 0) {System.out.println(number + " 是偶数");} else {System.out.println(number + " 是奇数");}}
}

在这里插入图片描述

【随堂练习】

  1. 模拟用户登录操作(用户名和密码都用字符串),需要判断出登录成功和登录失败的情况。
    注意:判断字符串是否相同,不能使用==,而是需要使用equals方法。

1.1.3 if多选结构

在这里插入图片描述
【语法结构】

if (条件表达式1) {// 条件1为true时执行的代码块
} else if (条件表达式2) {// 条件2为true时执行的代码块
} else if (条件表达式3) {// 条件3为true时执行的代码块
} else {// 所有条件都为false时执行的代码块
}

【工作原理】

  1. 执行流程:
    • 从上到下依次判断条件表达式
    • 遇到第一个值为true的条件,执行其对应的代码块
    • 执行完后直接跳出整个if结构
    • 如果所有条件都为false,则执行else块(如果存在)
  2. 每个条件表达式都必须是boolean类型
  3. 多个条件是互斥的,只会执行其中一个分支

【语法规范】

  1. 所有条件表达式必须用小括号()括起来
  2. else if和else必须紧跟在前一个分支的结束大括号后
  3. 建议始终使用大括号{},即使分支只有一条语句
  4. 所有分支应保持相同的缩进级别

【使用注意】

  1. else if分支可以有任意多个
  2. else分支是可选的
  3. 条件的判断顺序很重要,应该:
    • 将最可能满足的条件放在前面(提高效率)
    • 将特殊情况放在前面,一般情况放在后面
  4. 当条件之间有包含关系时,应该将范围小的放在前面

【示例代码】

/*** @Author: chenxiezhu* @Date: 2025/5/15 20:25* @Version: v1.0.0* @Description: TODO**/
public class IfElseIfExample {public static void main(String[] args) {// 示例1:年龄段判断int age = 25;if (age < 0) {System.out.println("年龄输入错误");} else if (age < 18) {System.out.println("未成年人");} else if (age < 35) {System.out.println("青年");} else if (age < 60) {System.out.println("中年");} else {System.out.println("老年");}// 示例2:会员等级折扣String memberLevel = "金牌";double originalPrice = 1000.0;double finalPrice;if (memberLevel.equals("钻石")) {finalPrice = originalPrice * 0.7;  // 7折} else if (memberLevel.equals("金牌")) {finalPrice = originalPrice * 0.8;  // 8折} else if (memberLevel.equals("银牌")) {finalPrice = originalPrice * 0.9;  // 9折} else {finalPrice = originalPrice;        // 原价}System.out.println("最终价格:" + finalPrice);}
}

在这里插入图片描述

【随堂练习】

  1. 判断一个学生的成绩,如果90(包含)100(包含)为优秀,70(包含)90为良好,60(包含)~70为及格,60分以下输出不及格。
  2. 输入一个整数month代表月份,根据月份输出对应的季节。 春季:3、4、5 夏季:6、7、8 秋季:9、10、11 冬季:12、1、2
  3. 录入一个年份,判断其是否为闰年。闰年的计算方法:年数能被4整除,并且不能被100整除;或者能被400整除的整数年份。

1.1.4 if选择结构总结

  1. 当if选择结构仅需执行单条语句时,大括号{}可以省略;但若需执行多条语句,则必须使用大括号{}。为保持代码的一致性和可读性,建议始终使用大括号,即使只有一条语句。
// 不推荐的写法
if (age >= 18)System.out.println("成年了");// 推荐的写法
if (age >= 18) {System.out.println("成年了");
}
  1. {}中的代码语句被称为代码块。在代码块中定义的常量或变量,其作用域仅限于该代码块内部,无法在代码块外部访问或使用。
if (condition) {int localVar = 100;  // localVar只在if块内有效System.out.println(localVar);
} // localVar在这里失效
// System.out.println(localVar); // 编译错误!// 正确的变量声明方式
int result;  // 在外部声明
if (condition) {result = 100;
} else {result = 0;
}
System.out.println(result);  // 可以使用
  1. 建议优先使用多分支if结构而非单分支if结构,因为多分支结构在程序执行效率上更具优势。
// 低效的写法
if (score >= 90) {grade = "A";
}
if (score >= 80) {grade = "B";
}
if (score >= 70) {grade = "C";
}// 高效的写法
if (score >= 90) {grade = "A";
} else if (score >= 80) {grade = "B";
} else if (score >= 70) {grade = "C";
} else {grade = "D";
}
  1. 在if选择结构中,可以灵活地嵌套其他选择结构或循环结构,从而实现更复杂的逻辑控制。
// if嵌套示例
if (isLoggedIn) {if (isAdmin) {System.out.println("管理员登录");for (User user : userList) {// 循环处理用户}} else {System.out.println("普通用户登录");while (hasUnreadMessages) {// 处理未读消息}}
}

1.2 switch选择结构

1.2.1 switch选择结构

【语法结构】

switch (表达式) {case 常量1:// 匹配常量1时执行的代码break;case 常量2:// 匹配常量2时执行的代码break;case 常量3:// 匹配常量3时执行的代码break;default:// 没有匹配时执行的默认代码break;
}

【工作原理】

  1. 执行流程:
    • 计算switch表达式的值
    • 依次与case常量进行匹配
    • 从匹配的case处开始执行,直到遇到break或结束
    • 如果没有匹配的case,则执行default块(如果存在)

【表达式类型限制】
switch表达式必须是以下类型之一:

  • byte、short、char、int
  • 枚举类型(enum)
  • String类型(Java 7及以上版本)
  • 包装类:Byte、Short、Character、Integer

【语法规范】

  1. case标签必须是常量表达式或常量值
  2. case标签值不能重复
  3. default分支可选,建议添加
  4. break语句可选,但建议每个分支都添加,避免贯穿
  5. case分支和default分支的顺序不影响执行

【使用注意】

  1. break的重要性:
    • 如果省略break,会发生case贯穿(fall-through)
    • 执行会继续进行直到遇到break或结束
  2. default位置:
    • 通常放在最后
    • 但可以放在任何位置
  3. 多个case可以共用同一段代码:

【示例代码】

/*** @Author: chenxiezhu* @Date: 2025/5/15 20:44* @Version: v1.0.0* @Description: TODO**/
public class SwitchDemo {public static void main(String[] args) {// 基础示例:根据星期几输出对应的活动安排int day = 3;switch (day) {case 1:System.out.println("周一:开始新的一周工作");break;case 2:System.out.println("周二:项目进度会议");break;case 3:System.out.println("周三:代码评审");break;case 4:System.out.println("周四:功能测试");break;case 5:System.out.println("周五:周报提交");break;case 6:case 7:System.out.println("周末:休息时间");break;default:System.out.println("无效的星期数");break;}}
}

在这里插入图片描述

/*** @Author: chenxiezhu* @Date: 2025/5/15 20:44* @Version: v1.0.0* @Description: TODO**/
public class SwitchDemo2 {public static void main(String[] args) {// 故意利用穿透效果的合理用法int month = 8;String season;switch (month) {case 12:case 1:case 2:season = "冬季";break;case 3:case 4:case 5:season = "春季";break;case 6:case 7:case 8:season = "夏季";break;case 9:case 10:case 11:season = "秋季";break;default:season = "无效月份";}// 输出判断结果System.out.println(month + " 月对应的季节是:" + season);}
}

在这里插入图片描述

【随堂练习】

  1. 输入一个整数,对应的显示出星期几,例如: 输入“1”,则输出为“星期一” 。
  2. 接收一个人的成绩,如果成绩为: 90(包含)到100(包含)输出优秀,70(包含)到90输出良好,60(包含)到70输出及格,60分以下输出不及格。
  3. 查询水果的价格,根据输入水果(fruit)的名字,输出对应的水果的价格,例如苹果6块/斤,香蕉3元/斤,榴莲20元/斤,西瓜0.8元/斤。

1.2.2 switch和if的区别

switch适用于对固定值进行判断的场景,它通过匹配特定值来执行相应的代码块。
if:则更适合处理区间或范围的判断,同时也可用于对boolean类型的表达式进行判断。
注意:switch能做的,if都能做,但反过来则不行。

2. 循环结构

2.1 for循环结构

【语法格式】

for (初始化表达式; 循环条件表达式; 更新表达式) {// 循环体语句
}

【执行流程】

  1. 执行初始化表达式(仅执行一次)
  2. 判断循环条件表达式:
    • 若为true,执行循环体
    • 若为false,退出循环
  3. 执行循环体语句
  4. 执行更新表达式
  5. 返回步骤2继续判断

【语法规范】

  1. 三个表达式都是可选的,但分号必须保留
  2. 初始化表达式可以声明多个变量,用逗号分隔
  3. 更新表达式可以包含多个操作,用逗号分隔
  4. 建议使用大括号{}包围循环体,即使只有一条语句

【使用注意】

  1. 循环变量的作用域:
    • 在for语句中声明的变量仅在循环内有效
    • 在外部声明的变量在循环外仍然有效
  2. 无限循环的写法:
for (;;) {// 无限循环
}
  1. 可以在循环体中使用break或continue控制循环流程

【示例代码】

/*** @Author: chenxiezhu* @Date: 2025/5/16 17:23* @Version: v1.0.0* @Description: TODO**/
public class ForLoopDemo {public static void main(String[] args) {// 打印1到5的数字for (int i = 1; i <= 5; i++) {System.out.println("当前数字:" + i);}}
}

在这里插入图片描述

执行流程详解:

// 执行流程示例
public void demonstrateForLoop() {/*执行顺序:1. 初始化:i = 0            (只执行一次)2. 检查条件:i < 3         (每次循环都检查)3. 执行循环体              (条件为true时执行)4. 执行递增:i++          (每次循环体执行后执行)5. 重复步骤2-4直到条件为false*/for (int i = 0; i < 3; i++) {System.out.println("第" + (i + 1) + "次循环");}
}

【随堂练习】

  1. 输出0(包含)到100(包含)之间的数,分别以递增和递减的方式实现;
  2. 输出1到100之间的奇数和偶数;
  3. 输入一个正整数n,计算1+2+3+…+n的和;
  4. 输入一个正整数n,计算1-2+3-4+5-6+…-(n-1)+n的和;
  5. 输出1到1000之间既能被5整除又能被3整除的数,并且每行输出5个。
  6. 求100到999之间的水仙花数。水仙花数的每个位上的数字的3次幂之和等于它本身(例如:13 + 53+33 = 153)。

2.2 while循环结构

【语法格式】

while (循环条件表达式) {// 循环体语句
}

【执行流程】

  1. 判断循环条件表达式:
    • 若为true,执行循环体
    • 若为false,退出循环
  2. 执行循环体语句
  3. 返回步骤1继续判断

【与for循环的区别】

  1. 结构特点:
    • while循环只有条件判断部分
    • 初始化和更新操作需要在循环外部或循环体内完成
  2. 适用场景:
    • while适合循环次数不确定的场景
    • for适合循环次数确定的场景

【语法规范】

  1. 条件表达式必须是boolean类型
  2. 建议使用大括号{}包围循环体
  3. 确保循环条件有机会变为false,避免死循环
  4. 循环变量的更新通常在循环体内完成

【示例代码】

/*** @Author: chenxiezhu* @Date: 2025/5/16 17:30* @Version: v1.0.0* @Description: TODO**/
public class WhileLoopDemo {public static void main(String[] args) {// 基础计数示例int count = 1;while (count <= 5) {System.out.println("当前计数:" + count);count++;}}
}

在这里插入图片描述

与for循环的区别示例代码:

// 1. while循环更适合的场景
public void whileExample() {// 未知迭代次数的场景Random random = new Random();int number = random.nextInt(100);while (number != 50) {number = random.nextInt(100);}
}// 2. for循环更适合的场景
public void forExample() {// 已知迭代次数的场景for (int i = 0; i < 10; i++) {System.out.println(i);}
}

选择使用while还是for循环,主要取决于具体的使用场景和代码的可读性需求。

// 1. 确保循环条件最终会变为false
public void ensureTermination() {int maxAttempts = 10;int attempts = 0;while (attempts < maxAttempts) {if (tryOperation()) {break;}attempts++;}
}// 2. 使用标志变量控制循环
public void flagControlled() {boolean isRunning = true;while (isRunning) {// 执行某些操作if (someCondition()) {isRunning = false;  // 设置标志以结束循环}}
}// 3. 防止死循环
public void preventInfiniteLoop() {int timeout = 30;  // 超时时间(秒)long startTime = System.currentTimeMillis();while (someCondition()) {// 检查是否超时if (System.currentTimeMillis() - startTime > timeout * 1000) {System.out.println("操作超时");break;}// 执行操作}
}

2.3 do-while循环结构

【语法结构】

do {// 循环体语句
} while (循环条件表达式);  // 注意这里的分号不能省略

【执行流程】

  1. 执行循环体语句
  2. 判断循环条件表达式:
    • 若为true,返回步骤1继续执行
    • 若为false,退出循环

【特点】

  1. 后测试循环:先执行,后判断
  2. 至少执行一次循环体
  3. 适用于循环体至少需要执行一次的场景

【与while循环的区别】

  1. while循环:先判断后执行
  2. do-while循环:先执行后判断
  3. do-while循环至少执行一次,while循环可能一次都不执行

【语法规范】

  1. while后的分号必须有
  2. 条件表达式必须是boolean类型
  3. 建议使用大括号{}包围循环体

【示例代码】

/*** @Author: chenxiezhu* @Date: 2025/5/16 17:45* @Version: v1.0.0* @Description: TODO**/
public class DoWhileDemo {public static void main(String[] args) {// 基础计数示例int count = 1;do {System.out.println("当前计数:" + count);count++;} while (count <= 5);}
}

在这里插入图片描述

与while循环的区别示例代码:

// 1. do-while适合的场景
public void doWhileExample() {int number;// 确保至少获取一次输入do {System.out.print("请输入正数:");number = scanner.nextInt();} while (number <= 0);
}// 2. while适合的场景
public void whileExample() {// 可能一次都不执行的情况while (hasMoreData()) {processData();}
}

【注意事项】

  1. 确保循环体中包含能改变循环条件的代码
  2. 适当处理异常情况
  3. 考虑添加超时或最大重试次数限制
  4. 提供清晰的用户反馈
  5. 在循环条件中使用简单且明确的逻辑

do-while循环特别适合需要至少执行一次的场景,如用户输入验证、菜单驱动的程序等。选择使用do-while还是其他循环结构,主要取决于业务逻辑的需求和代码的可读性。

// 1. 错误处理和重试逻辑
public void robustOperation() {int retryCount = 0;boolean success;do {try {performOperation();success = true;} catch (Exception e) {retryCount++;success = false;System.out.println("发生错误:" + e.getMessage());if (retryCount >= 3) {throw new RuntimeException("操作失败次数过多");}}} while (!success);
}// 2. 输入验证
public int getValidAge() {int age;do {System.out.print("请输入年龄(0-120):");age = scanner.nextInt();if (age < 0 || age > 120) {System.out.println("输入无效,请重试!");}} while (age < 0 || age > 120);return age;
}// 3. 避免无限循环
public void safeLoop() {int timeout = 10; // 10秒超时long startTime = System.currentTimeMillis();do {// 执行操作if (System.currentTimeMillis() - startTime > timeout * 1000) {System.out.println("操作超时");break;}} while (someCondition());
}

2.4 break和continue详解

2.4.1 break关键字

break语句的作用:

  1. 立即终止并跳出当前循环或switch语句
  2. 执行循环或switch语句后面的代码

break的使用规则:

  1. 只能在循环结构(for、while、do-while)和switch语句中使用
  2. 在不同结构中的效果:
    • 循环中:直接跳出当前循环,不执行循环中剩余的语句
    • switch中:终止switch语句的执行
    • for循环中:不会执行"循环后的操作表达式"

注意事项:

  • break后面不能有其他语句,因为这些语句永远不会被执行
  • 在多重循环中,break只能跳出当前最内层的循环(除非使用标签)
  • break不能用于if语句(除非if语句包含在循环或switch中)

【示例代码】

/*** @Author: chenxiezhu* @Date: 2025/5/16 18:04* @Version: v1.0.0* @Description: TODO**/
public class BreakDemo {public static void main(String[] args) {// 1. 简单break示例System.out.println("1.1 基础break用法:");for (int i = 1; i <= 10; i++) {if (i == 5) {break;}System.out.print(i + " ");}// 2. break在嵌套循环中的使用System.out.println("\n\n1.2 嵌套循环中的break:");for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {if (i * j == 4) {System.out.println("遇到i*j=4,结束内层循环");break;}System.out.printf("i=%d, j=%d\n", i, j);}}// 3. break在switch中的使用System.out.println("\n1.3 switch中的break:");for (int choice = 1; choice <= 3; choice++) {System.out.print("当choice=" + choice + "时:");switch (choice) {case 1:System.out.println("选择1");break;case 2:System.out.println("选择2");break;default:System.out.println("其他选择");}}}
}

在这里插入图片描述

2.4.2 continue关键字

continue语句用在循环语句体中,作用是:

  1. 立即终止当前循环的本次迭代
  2. 跳过循环体中continue后尚未执行的语句
  3. 直接进入下一次循环的判定

continue的使用规则:

  1. 只能在循环结构中使用(for、while、do-while)
  2. 在不同循环中的效果:
    • for循环:跳到更新语句处
    • while/do-while:跳到条件判断处
  3. 可以与标签(label)配合使用在嵌套循环中

注意事项:

  • continue不会终止循环的整体执行,只是跳过当次循环的剩余代码
  • 在多重循环中,continue只对最内层循环起作用(除非使用标签)
/*** @Author: chenxiezhu* @Date: 2025/5/16 18:06* @Version: v1.0.0* @Description: TODO**/
public class ContinueDemo {public static void main(String[] args) {// 1. 简单continue示例System.out.println("2.1 基础continue用法:");for (int i = 1; i <= 5; i++) {if (i == 3) {continue;}System.out.print(i + " ");}// 2. continue在while循环中的使用System.out.println("\n\n2.2 while循环中的continue:");int i = 0;while (i < 5) {i++;if (i == 3) {continue;}System.out.print(i + " ");}// 3. continue在嵌套循环中的使用System.out.println("\n\n2.3 嵌套循环中的continue:");for (int x = 0; x < 3; x++) {for (int y = 0; y < 3; y++) {if (x == y) {continue;}System.out.printf("x=%d, y=%d\n", x, y);}}}
}

在这里插入图片描述

【示例代码】

2.4.3 带标签的break和continue

goto关键字很早就在程序设计语言中出现。尽管goto仍是Java的一个保留字,但并未在语言中得到正式使用(Java没有goto)。然而,在break和continue这两个关键字的身上,我们仍然能看出一些goto的影子,那就是带标签的break和continue。

"标签"是指后面跟一个冒号的标识符,例如:“label:”。在Java中,标签主要用于循环语句之前。使用标签的主要场景是在嵌套循环中,因为:

  1. break通常只能跳出当前最内层循环,带标签的break可以直接跳出指定的外层循环
  2. continue通常只能继续当前最内层循环的下一次迭代,带标签的continue可以直接进入指定外层循环的下一次迭代

需要注意的是:

  • 标签必须放在循环语句之前才有意义
  • 标签的使用虽然强大,但过度使用会降低代码的可读性
  • 建议仅在确实需要控制外层循环时才使用标签

【示例代码】

/*** @Author: chenxiezhu* @Date: 2025/5/16 17:56* @Version: v1.0.0* @Description: TODO**/
public class LabeledBreakContinueDemo {public static void main(String[] args) {// 1. 带标签的breakSystem.out.println("3.1 带标签的break:");outer: for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {if (i * j == 4) {System.out.println("遇到i*j=4,跳出外层循环");break outer;}System.out.printf("i=%d, j=%d\n", i, j);}}// 2. 带标签的continueSystem.out.println("\n3.2 带标签的continue:");outer: for (int i = 0; i < 3; i++) {System.out.println("外层循环 i=" + i);for (int j = 0; j < 3; j++) {if (i == 1) {System.out.println("i=1,继续外层循环");continue outer;}System.out.printf("i=%d, j=%d\n", i, j);}}// 3. 多重嵌套循环中的标签System.out.println("\n3.3 多重嵌套循环中的标签:");outermost: for (int i = 0; i < 3; i++) {middle: for (int j = 0; j < 3; j++) {for (int k = 0; k < 3; k++) {if (i * j * k == 4) {System.out.println("遇到i*j*k=4,跳出最外层循环");break outermost;}if (j == 1) {System.out.println("j=1,继续中层循环");continue middle;}System.out.printf("i=%d, j=%d, k=%d\n", i, j, k);}}}}
}

在这里插入图片描述

【注意事项】

  1. break会完全跳出当前循环或switch
  2. continue只跳过当前迭代的剩余代码
  3. 标签虽然强大,但应谨慎使用,过度使用会降低代码可读性
  4. 在可能的情况下,优先考虑使用方法提取来替代复杂的break/continue逻辑

【随堂练习】

  1. 使用continue实现输出1到100之间能被5整除的数。
  2. 韩信点兵,三人一组余两人,五人一组余三人,七人一组余四人,请问最少需要多少士兵。
  3. [难]输入一个任意整数,判断它是否为质数。如果整数为7,则输出“7是质数”,如果整数为9,则输出‘9不是质数’。质数:除了1和它本身之外,不能被其他数整除的数称之为质数。

实现步骤:

  • 通过Scanner获取一个正整数, 假设正整数为num。
  • 通过循环获得[2, num)之间所有的整数,假设循环变量为i。
  • 在循环体内部判断num是否能被i整除,也就是判断num是否为质数。

实现思路:采用假设法来实现。

  • 在循环之前,先假设num是一个质数,设置flag标记的值为true
  • 在循环体中,如果num%i==0,则推翻假设,设置flag标记的值为false,并跳出循环
  • 在循环之后,如果flag值依旧为true,则证明是一个质数,否则证明不是一个质数。

2.5 while与for的区别

for和while的区别:

  • for循环能做的事情while循环都可以实现,它们在功能上是等价的。

建议使用while循环的场景:

  1. 循环结束后循环变量还需要参与运算。
  2. 不确定循环次数的情况(如读取文件、用户输入等)。
  3. 循环条件比较简单,只需要一个布尔表达式

建议使用for循环的场景:

  1. 循环次数确定的场景。
  2. 循环变量的初始化、条件判断和更新操作关系紧密。
  3. 循环变量在循环结束后不需要使用。
  4. 需要同时使用多个循环变量的情况。

选择依据:

  • while循环结构更适合"当满足某条件时,就重复执行某事"的场景
  • for循环结构更适合"重复做某事n次"的场景

2.6 嵌套循环

嵌套循环的定义:

  1. 在一个循环语句内部包含另一个或多个循环语句
  2. while、do-while和for循环可以任意组合嵌套,没有层数限制

执行特点:

  1. 外层循环执行一次,内层循环要完整执行一遍
  2. 内层循环的循环次数 = 内层循环条件满足的次数 × 外层循环的次数
  3. 执行顺序:从外到内开始循环,从内到外结束循环

注意事项:

  • 每层循环体都应该有独立的循环变量
  • 内层循环的循环变量要在外层循环的每次迭代中重新初始化(如果需要)
  • 可以使用break/continue配合标签来控制外层循环
  • 嵌套层数过多会降低代码的可读性和维护性,建议适度使用

性能考虑:

  • 嵌套循环的时间复杂度是各层循环次数的乘积
  • 在实际编程中应注意优化嵌套循环的执行效率

【随堂练习】

  1. 在控制台先打印矩形,然后再打印平行四边形,再然后打印等腰三角形。
    在这里插入图片描述

  2. 使用嵌套循环实现九九乘法表(注意循环变量的初始值)。
    在这里插入图片描述

3. 方法(method)

3.1 方法的概念与特征

【定义】
方法是类中定义的用于完成特定功能的代码块,是面向对象编程中实现行为的基本单元。它封装了一系列操作,提供代码重用和模块化的机制。

【方法的特征】

  1. 封装性:将特定功能的代码封装为独立单元
  2. 重用性:可以被多次调用
  3. 模块化:便于程序的维护和管理
  4. 作用域:在类中定义,可以被类内部或外部调用(取决于访问修饰符)

【main方法】

public static void main(String[] args) {// 程序入口点
}

特点:

  1. public:访问权限修饰符,确保JVM可以访问
  2. static:静态方法,不需要创建类实例即可执行
  3. void:无返回值
  4. main:方法名,固定写法
  5. String[] args:命令行参数数组

3.2 方法声明&调用

3.2.1 方法声明格式

【基本语法】

[访问修饰符] [其他修饰符] 返回值类型 方法名([参数列表]) [throws 异常类型] {// 方法体[return 返回值;]
}

【格式说明】

  1. 修饰符,用于控制方法的访问权限(后面的课程会细讲)。
  2. 返回值类型,方法需要返回值的数据类型,如无返回值,必须声明返回值类型为void。
  3. 方法名,方法的名字,要求必须符合标识符规范,并且采用小驼峰命名法则。
  4. 形参列表,由参数类型和参数名组成,形参可以为任意多个,用于给方法内部传递数据。
  5. 执行语句,书写方法需要实现的功能操作。
  6. 返回值,如果定义了返回值类型,那么返回值和返回值类型必须保持一致;如果定义的返回值类型为void,那么需要省略返回值,也就是直接返回return ;即可

注意:当方法返回值类型为void时,还可以省略return;

3.2.2 方法调用格式

方法名(实际参数1, 实际参数2, ……);

实际参数(简称实参):传递给形式参数的具体数值,对应着形式参数(简称形参)。
注意事项:

  1. 形式参数(形参)和实际参数(实参)的类型和个数必须一一对应。
  2. 在方法中只能调用方法,不可以在方法内部再定义方法。
  3. 当方法有返回值类型时,可以用一个变量用于接收方法的返回值,该变量的类型必须和方法返回值类型保持一致。

3.3 方法的运用

方法的使用,首先明确两个需求:

  1. 明确要定义的功能最后的结果是什么?
  2. 明确在定义该功能的过程中,是否需要未知内容参与运算?

方法的使用分为两个步骤:

  1. 定义方法
  2. 调用方法

定义方法,根据是否有参数列表、是否有返回值,可将定义方法分为四类:

  1. 无参无返回值方法
  2. 无参有返回值方法
  3. 有参无返回值方法
  4. 有参带返回值方法

调用方法,根据方法是否有返回值,可将调用方法分为两类:

  1. 有返回值方法,可以用一个变量来接收返回值,也可以忽略返回值。
  2. 无返回值方法,不能用一个变量来接收方法的void返回值。

3.3.1 无参无返回值方法

需求:在方法中输出“hello world”。
分析:不需要参数和返回值,应该选用无参无返回值方法。

【示例代码】

// 需求:在方法中输出“hello world”。
public static void main(String[] args) {// 调用方法show(); 
}
// 声明方法:无参无返回值方法
public static void show() {System.out.println("hello world");
}

3.3.2 无参有返回值方法

需求:调用方法获得常量3+4的和。
分析:不需要外界传入参数,但是需要返回常量3+4之和。

【示例代码】

// 需求:调用方法获得常量3+4的和。
public static void main(String[] args) {// 调用方法:可以选择用一个变量接收返回值,也可以忽略返回值int num = add(); // 接收返回值add(); // 忽略返回值
}
// 声明方法:无参有返回值方法
public static int add() {return 3 + 4;
}

3.3.3 有参无返回值方法

需求:在方法中输出指定两个int变量的和。
分析:需要外界传入两个int类型的参数,但不需要返回值。

【示例代码】

// 需求:在方法中输出指定两个int变量的和。
public static void main(String[] args) {// 定义两个变量int a = 3, b = 4;// 调用方法add(a, b);
}
// 声明方法:有参无返回值方法
public static void add(int num1, int num2) {int sum = num1 + num2;System.out.println("sum: " + sum);
}

3.3.4 有参有返回值方法

需求:调用方法获得指定两个double变量的和。
分析:需要外界传入两个double类型的形参,并返回两个double类型形参之和。
【示例代码】

// 需求:调用方法获得指定两个double变量的和。
public static void main(String[] args) {// 定义两个变量double a = 3, b = 4;// 调用方法:可以选择用一个变量接收返回值,也可以忽略返回值double sum = add(a, b); // 接收返回值add(a, b); // 忽略返回值
}
// 声明方法:有参有返回值方法
public static double add(double num1, double num2) {return num1 + num2;
}

3.4 方法调用过程

当一个方法被调用时,JVM会完成以下步骤:

  1. 在栈内存中为方法创建一个新的栈帧
  2. 将实参值传递给形参变量
  3. 执行方法体中的代码
  4. 返回结果(如果有)
  5. 栈帧被销毁,释放内存空间

https://www.bilibili.com/video/BV1a5411y77c?spm_id_from=333.788.videopod.episodes&vd_source=0014532c22de121c0f23cdb7aaa185bb&p=96

3.5 方法的重载

3.5.1 重载的概念

在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
核心为两同两不同:

  1. 同一个类,同一个方法名
  2. 参数列表不同(类型、个数)

3.5.2 重载的特点

返回值类型不同,不构成方法的重载。
形参的名称不同,不构成方法的重载。
方法修饰符不同,不构成方法的重载。
注意:重载的方法,实际是完全不同的方法!只是方法名称相同而已!

【示例代码】

// 方法重载示例
public class OverloadDemo {public static void main(String[] args) {System.out.println(add(10, 20));         // 调用第一个add方法System.out.println(add(10, 20, 30));     // 调用第二个add方法System.out.println(add(10.5, 20.5));     // 调用第三个add方法}// 两个int参数的add方法public static int add(int a, int b) {return a + b;}// 三个int参数的add方法public static int add(int a, int b, int c) {return a + b + c;}// 两个double参数的add方法public static double add(double a, double b) {return a + b;}
}

3.5.3 重载的好处

查看JDK文档中System.out.println()方法,发现共有10个println()方法构成了“重载关系”,如下图:
在这里插入图片描述
方法重载通常用来命名一组功能相似的方法,这样做减少了方法名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。

3.6 递归(recursion)

3.6.1 递归的定义

在方法体中,直接或间接调用方法本身,我们就称之为“递归”(核心:自己调用自己)。

3.6.2 递归的使用

使用递归可以用简单逻辑来解决一些复杂的问题,使用递归则必须用到方法,而方法体中必须包含“递归头”和“递归体”,否则使用递归就会陷入死循环。

  • 递归头
    明确什么时候结束自己调用自己(出口)。
  • 递归体
    明确什么时候执行自己调用自己(出口)
/*** @Author: chenxiezhu* @Date: 2023/5/16 21:30* @Version: v1.0.0* @Description: 递归示例**/
public class RecursionDemo {public static void main(String[] args) {// 示例1:计算阶乘int n = 5;System.out.println(n + "的阶乘是:" + factorial(n));// 示例2:斐波那契数列System.out.println("斐波那契数列的第" + n + "个数是:" + fibonacci(n));// 示例3:打印斐波那契数列前10项System.out.println("斐波那契数列前10项:");for (int i = 1; i <= 10; i++) {System.out.print(fibonacci(i) + " ");}}/*** 计算阶乘的递归方法* @param n 要计算阶乘的数* @return n的阶乘结果*/public static int factorial(int n) {// 递归头:明确结束条件if (n <= 1) {return 1;}// 递归体:调用自身return n * factorial(n - 1);}/*** 计算斐波那契数列的递归方法* 斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, ...* 规律:从第3项开始,每一项等于前两项之和* @param n 第n个斐波那契数* @return 斐波那契数列的第n个数*/public static int fibonacci(int n) {// 递归头:明确结束条件if (n <= 2) {return 1;}// 递归体:调用自身return fibonacci(n - 1) + fibonacci(n - 2);}
}

【随堂练习】

  1. 计算n的阶乘,也就是获得123*…*(n-1)*n的结果。
  2. 假如有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,请问第n个月后的兔子有多少对?

3.6.3 递归的缺陷

简单的程序是递归的优点之一。但是递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环慢的多。所以再使用时要慎重,在要求高性能的情况下尽量避免使用递归。

http://www.xdnf.cn/news/501211.html

相关文章:

  • Netty的简单使用
  • 在RK3588上使用NCNN和Vulkan加速ResNet50推理全流程
  • PHP8.0版本导出excel失败
  • 高并发内存池|定长内存池的设计
  • 如何在前端页面上展示解析后的 JSON 数据?
  • deepin v23.1 音量自动静音问题解决
  • 关系代数和关系数据库语言(SQL)
  • 力扣HOT100之二叉树:108. 将有序数组转换为二叉搜索树
  • BUG调试案例一:为什么网卡芯片可以找到却PING不通?--RK3588+YT8531网络调试实录
  • 算法:分治法
  • 单调栈和单调队列
  • 使用instance着色
  • 高效完成任务:制定标准与限时完成的双重法宝
  • lc42接雨水
  • 阿里巴巴开源移动端多模态LLM工具——MNN
  • Dockerfile学习指南
  • 搜索引擎工作原理|倒排索引|query改写|CTR点击率预估|爬虫
  • Linux面试题集合(4)
  • 木材价格动态定价实战指南:多算法模型与行业案例深度解析
  • 算法题(148):排座椅
  • 实验八 基于Python的数字图像问题处理
  • MySQL 中 JOIN 和子查询的区别与使用场景
  • 基于 Leaflet 地图库的强大线条、多边形、圆形、矩形等绘制插件Leaflet-Geoman
  • [强化学习的数学原理—赵世钰老师]学习笔记02-贝尔曼方程
  • 《算法导论(第4版)》阅读笔记:p82-p82
  • 如何免费在线PDF转换成Excel
  • Java并发编程的挑战:从理论到实战
  • 题单:汉诺塔问题
  • 使用Langfuse和RAGAS,搭建高可靠RAG应用
  • ctfshow——web入门254~258