选择意味着取舍——Miao酱
- 程序设计中有三种流程结构,即:
- 顺序结构
- 程序从上到下逐行地执行,中间没有任何判断和跳转。
- 分支结构
- 根据判断条件,选择性地执行某段代码。
- 有
if…else
和switch-case
两种分支语句。
- 循环结构
- 根据循环条件,重复性的执行某段代码。
- 有
for
、while
、do-while
、foreach
四种循环语句。
- 顺序结构
1. 顺序结构
顺序结构很好理解,即程序从上到下逐行执行,直到最终结束。
public class StatementTest{
public static void main(String[] args){
int num1 = 1;
int num2 = 2;
int num3 = num1 + num2;
System.out.println("num1 + num2 = " + num3);
}
}
2. 分支结构(Conditional Statements)
分支结构也很好理解,类似于数学中的分类讨论,以解一元二次方程为例,当\(\Delta > 0\)时, 有两个不同的解,\(\Delta = 0\)时,有一个解,\(\Delta < 0\)时,方程无解。
2.1 if-else
2.1.1 基本语法
结构1:单分支条件判断:if
if(条件表达式){
语句块;
}
eg. 高考成绩大于600分,则可以上985
import java.util.Scanner;
public class Test1{
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
System.out.println("请输入高考分数:");
int score = scanner.nextInt();
if (score >= 600){
System.out.println("可以上985");
}
scanner.close();
}
}
结构2:双分支条件判断:if...else
if(条件表达式) {
语句块1;
}else {
语句块2;
}
eg. 判断一个整数是偶数还是奇数
import java.util.Scanner;
public class Test2{
public static void main(String[] args){
Scanner scanner=new Scanner(System.in);
System.out.println("请输入一个整数:");
int num=scanner.nextInt();
if(num % 2 == 0) {
System.out.println(num + "是偶数");
} else{
System.out.println(num + "是奇数");
}
scanner.close();
}
}
结构3:多分支条件判断:if...else if...else
if (条件表达式1) {
语句块1;
} else if (条件表达式2) {
语句块2;
}
...
}else if (条件表达式n) {
语句块n;
} else {
语句块n+1;
}
eg. 小明参加期末Java考试,通过考试成绩,判断其Java等级,成绩范围[0,100]
- 90-100 A
- 80-89 B
- 70-79 C
- 60-69 D
- 60以下 E
import java.util.Scanner;
//写法一:
public class Test3{
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入期末成绩:");
int score = scanner.nextInt();
if (score < 0 || score > 100) {
System.out.println("成绩有误");
} else if (score >= 90 && score <= 100) {
System.out.println("你的成绩为 A");
} else if (score >= 80 && score < 90) {
System.out.println("你的成绩为 B");
} else if (score >= 70 && score < 80) {
System.out.println("你的成绩为 C");
} else if (score >= 60 && score < 70) {
System.out.println("你的成绩为 D");
} else {
System.out.println("你的成绩为 E");
}
scanner.close();
}
}
import java.util.Scanner;
//写法二:
public class Test4{
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入期末成绩:");
int score = scanner.nextInt();
if (score < 0 || score > 100) {
System.out.println("成绩有误");
} else if (score >= 90 && score <= 100) {
System.out.println("你的成绩为 A");
} else if (score >= 80 && score < 90) {
System.out.println("你的成绩为 B");
} else if (score >= 70 && score < 80) {
System.out.println("你的成绩为 C");
} else if (score >= 60 && score < 70) {
System.out.println("你的成绩为 D");
} else {
System.out.println("你的成绩为 E");
}
scanner.close();
}
}
当条件表达式之间是互斥关系时,条件判断语句及执行语句间顺序无所谓。
当条件表达式之间是包含关系时,则按照“小上大下 / 子上父下”的顺序,否则范围小的条件表达式将不可能被执行。
2.2 switch-case
2.2.1 基本语法
switch(表达式){
case 常量值1:
语句块1;
//break;
case 常量值2:
语句块2;
//break;
// ...
[default:
语句块n+1;
break;
]
}
具体执行过程:
-
根据switch中表达式的值,依次匹配各个case。如果表达式的值等于某个case中的常量值,则执行对应case中的执行语句。
-
执行完此case的执行语句以后,
情况1:如果遇到break,则执行break并跳出当前的switch-case结构
情况2:如果没有遇到break,则会继续执行当前case之后的其它case中的执行语句。--->case穿透
...
直到遇到break关键字或执行完所有的case及default的执行语句,跳出当前的switch-case结构
注意
-
switch中表达式的值必须是下述几种类型之一:byte,short,char,int,枚举 (jdk 5.0),String (jdk 7.0);
-
case子句中的值必须是常量,不能是变量名或不确定的表达式值或范围;
-
同一个switch语句,所有case子句中的常量值互不相同;
-
break语句用来跳出switch语句块,如果没有break,程序会顺序执行到switch结尾;
-
default子句是可选的。同时,位置也是灵活的。当没有匹配的case时,执行default语句。
错误举例:
// 判断是正数、负数、还是零
int key = 10;
switch(key){
case key > 0 :
System.out.println("正数");
break;
case key < 0:
System.out.println("负数");
break;
default:
System.out.println("零");
break;
}
此种情况只能使用if-else语句进行条件判断
2.2.2 case的穿透性
案例1: 使用switch-case实现:对学生成绩大于60分的,输出“合格”。低于60分的,输出“不合格”。
public class SwitchCaseTest1{
public static void main(String[] args) {
int score = 67;
/*
写法1:极不推荐
switch(score){
case 0:
System.out.println("不及格");
break;
case 1:
System.out.println("不及格");
break;
//...
case 60:
System.out.println("及格");
break;
//...略...
}
*/
//写法2 利用case穿透
switch(score / 10){
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
System.out.println("不及格");
break;
case 6:
case 7:
case 8:
case 9:
case 10:
System.out.println("及格");
break;
default:
System.out.println("输入的成绩有误");
break;
}
//写法3:
switch(score / 60){
case 0:
System.out.println("不及格");
break;
case 1:
System.out.println("及格");
break;
default:
System.out.println("输入的成绩有误");
break;
}
}
}
案例2: 从键盘上输入2024年的“month”和“day”,计算输入的日期为2024年的第几天。
import java.util.Scanner;
class SwitchCaseTest2{
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.println("请输入2024年的month:");
int month = scan.nextInt();
System.out.println("请输入2024年的day:");
int day = scan.nextInt();
int sumDays = 0;
//利用case穿透
switch(month){
case 12:
sumDays += 30;//这个30是代表11月份的满月天数
case 11:
sumDays += 31;//这个31是代表10月份的满月天数
case 10:
sumDays += 30;//这个30是代表9月份的满月天数
case 9:
sumDays += 31;//这个31是代表8月份的满月天数
case 8:
sumDays += 31;//这个31是代表7月份的满月天数
case 7:
sumDays += 30;//这个30是代表6月份的满月天数
case 6:
sumDays += 31;//这个31是代表5月份的满月天数
case 5:
sumDays += 30;//这个30是代表4月份的满月天数
case 4:
sumDays += 31;//这个31是代表3月份的满月天数
case 3:
sumDays += 29;//这个29是代表2月份的满月天数
case 2:
sumDays += 31;//这个31是代表1月份的满月天数
case 1:
sumDays += day;//这个day是代表当月的第几天
}
System.out.println(month + "月" + day + "日是2024年的第" + sumDays + "天");
//关闭资源
scan.close();
}
}
2.2.3 if-else语句与switch-case语句比较
-
凡是使用switch-case的结构都可以转换为if-else结构。反之,不成立。
-
开发经验:如果既可以使用switch-case,又可以使用if-else,建议使用switch-case。因为效率稍高。
-
细节对比:
- if-else 语句优势
- if语句的条件是一个布尔类型值,if条件表达式为true则进入分支,可以用于范围的判断,也可以用于等值的判断,
使用范围更广
。 - switch语句的条件是一个常量值(byte,short,int,char,枚举,String),只能判断某个变量或表达式的结果是否等于某个常量值,
使用场景较狭窄
。
- if语句的条件是一个布尔类型值,if条件表达式为true则进入分支,可以用于范围的判断,也可以用于等值的判断,
- switch 语句优势
- 当条件是判断某个变量或表达式是否等于某个固定的常量值时,使用if和switch都可以,习惯上使用switch更多。因为
效率稍高
。当条件是区间范围的判断时,只能使用if语句。 - 使用switch可以利用
穿透性
,同时执行多个分支,而if...else没有穿透性。
- 当条件是判断某个变量或表达式是否等于某个固定的常量值时,使用if和switch都可以,习惯上使用switch更多。因为
- if-else 语句优势
3. 循环结构(Loops)
- 循环结构分类:
- for 循环
- while 循环
- do-while 循环
- foreach 循环
3.1 for循环
3.1.1 基本语法
语法格式:
for (①初始化部分; ②循环条件部分; ④迭代部分){
③循环体部分;
}
执行过程: ①-②-③-④-②-③-④-②-③-④-.....-②
说明:
- for(;;)中的两个;不能多也不能少。
- 初始化部分可以声明多个变量,但必须是同一个类型,用逗号分隔。
- 循环条件部分为boolean类型表达式,当值为false时,退出循环。
- 可以有多个变量更新,用逗号分隔。
3.2 while循环
3.2.1 基本语法
语法格式:
①初始化部分
while(②循环条件部分){
③循环体部分;
④迭代部分;
}
执行过程: ①-②-③-④-②-③-④-②-③-④-...-②
说明:
- while(循环条件)中循环条件必须是boolean类型。
- 不要忘记声明④迭代部分。否则,循环将不能结束,变成死循环。
- for循环和while循环可以相互转换。二者没有性能上的差别。
- for循环与while循环的区别:初始化条件部分的作用域不同。
3.3 do-while循环
3.3.1 基本语法
语法格式:
①初始化部分;
do{
③循环体部分
④迭代部分
}while(②循环条件部分);
执行过程: ①-③-④-②-③-④-②-③-④-...-②
说明:
- 结尾while(循环条件)中循环条件必须是boolean类型
- do{}while();最后有一个分号
- do-while结构的循环体语句是至少会执行一次,这个和for和while是不一样的
- 循环的三个结构for、while、do-while三者是可以相互转换的。
3.4 对比三种循环结构
-
三种循环结构都具有四个要素:
- 循环变量的初始化条件
- 循环条件
- 循环体语句块
- 循环变量的修改的迭代表达式
-
从循环次数角度分析
- do-while循环至少执行一次循环体语句
- for和while循环先判断循环条件语句是否成立,然后决定是否执行循环体
-
如何选择
- 遍历有明显的循环次数(范围)的需求,选择for循环
- 遍历没有明显的循环次数(范围)的需求,选择while循环
- 如果循环体语句块至少执行一次,可以考虑使用do-while循环
- 本质上:三种循环之间完全可以互相转换,都能实现循环的功能
案例:累加的思想
题目:遍历1-100以内的偶数,并获取偶数的个数,获取所有的偶数的和
// 写法一:for
public class ForTest{
public static void main(String[] args) {
int count = 0;// 记录偶数的个数
int sum = 0;// 记录偶数的和
for(int i = 1;i <= 100;i++){// 初始化、循环条件、迭代部分
// 循环体
if(i % 2 == 0){
System.out.println(i);
count++;
sum += i;
}
}
System.out.println("偶数的个数为:" + count);
System.out.println("偶数的总和为:" + sum);
}
}
// 写法二:while
public class WhileTest{
public static void main(String[] args) {
int num = 1;// 初始化部分
int sum = 0;// 记录偶数和
int count = 0;// 记录偶数的个数
while(num <= 100){// 循环条件
// 循环体
if(num % 2 == 0){
System.out.println(num);
sum += num;
count++;
}
// 迭代部分
num++;
}
System.out.println("偶数的个数为:" + count);
System.out.println("偶数的总和为:" + sum);
}
}
// 写法三:do while
public class DoWhileTest{
public static void main(String[] args) {
int num = 1;// 初始化部分
int sum = 0;// 记录偶数和
int count = 0;// 记录偶数的个数
do{
// 循环体部分
if(num % 2 == 0){
System.out.println(num);
sum += num;
count++;
}
num++;// 迭代部分
}while(num <= 100); // 循环条件部分
System.out.println("偶数的总和为:" + sum);
System.out.println("偶数的个数为:" + count);
}
}
3.5 for each
for循环经常通过索引来遍历数组,但是我们实际想访问的是数组中的元素,故java提供了另一种循环,可以更加方便的便利数组。
public class ForEachTest{
public static void main(String[] args) {
int[] arr = { 1, 3, 5, 7, 9 };
for (int n : arr) {
System.out.println(n);
}
}
}
for each循环的变量n不再是计数器,而是直接对应到数组的每个元素。除了数组外,for each循环能够遍历所有“可迭代”的数据类型,例如List、Map等集合框架。
3.6 "无限"循环
语法格式:
while(true)
,for(;;)
适用场景:
- 开发中,有时并不确定需要循环多少次,需要根据循环体内部某些条件,来控制循环的结束(使用break)。
- 如果此循环结构不能终止,则构成了死循环!开发中要避免出现死循环。
4. 关键字break & continue
4.1 break和continue的使用
在流程控制语句中,当程序满足某些条件时,如何才能提前
结束流程控制语句,这就涉及到关键字break和continue的使用。具体如下:
- break:结束或跳出当前的循环,适用范围为switch-case、循环结构
- continue: 结束或跳出本次循环,适用范围为循环结构
举个例子:
// break
public class BreakTest1{
public static void main(String[] args) {
for (int i=1; i<=10; i++) {
if (i % 5 == 0) {
break; // break 通常搭配if使用,用于跳出当前循环
// 当break跳出循环后,这行语句不会被执行,且编译器报错
//System.out.println("#####");
}
System.out.println(i); // 1 2 3 4
}
}
}
public class BreakTest2{
public static void main(String[] args) {
for (int i=1; i<=5; i++) {
for (int j=1; j<=10; j++) {
if (j % 5 == 0) {
break; // break跳出内循环
}
System.out.println("j = " + j);
}
// break跳到这里
System.out.println("breaked");
}
}
}
// continue
public class ContinueTest1{
public static void main(String[] args) {
for (int i=1; i<=10; i++) {
if (i % 5 == 0) {
continue; // continue 通常搭配if使用,用于跳出本次循环
}
System.out.println(i); // 1 2 3 4 6 7 8 9
}
}
}
4.2 带标签
当有多重循环时,break只能跳出最近的一层循环,那如何才可以跳出整个循环呢,我们可以使用label来实现。类似于goto语句,但是在java中却不允许使用(goto为保留字)。代码如下:
class BreakContinueTest{
public static void main(String[] args) {
label:for(int i = 1;i <= 4;i++){
for(int j = 1;j <= 10;j++){
if(j % 4 == 0){
//break label; // 123
continue label; // 123123123123
}
System.out.print(j);
}
}
}
}
- 标号语句必须紧接在循环的头部。标号语句不能用在非循环语句的前面。
- continue语句出现在多层嵌套的循环语句体中时,也可以通过标签指明要跳过的是哪一层循环。