流程控制语句是用来控制程序中各语句执行顺序的语句,可以把语句组合成能完成一定功能的小逻辑模块。
流程控制方式采用结构化程序设计中规定的三种基本流程结构:
- 顺序结构
- 选择结构
- 循环结构
顺序结构
顺序结构:程序从上到下逐行地执行,中间没有任何判断和跳转。
一个简单应用:Java中定义成员变量时采用合法的前向引用,即变量先定义再使用
选择结构
选择结构:根据条件,选择性地执行某段代码。有if…else和switch-case两种分支语句。
if-else结构
if语句有三种形式:
结构一:
if(条件表达式){
执行代码块;
}
流程图如下:
结构二:
if( 条件表达式){
执行代码块1;
}else{
执行代码块2;
}
流程图如下:
![image-20210402190137279](https://i-blog.csdnimg.cn/blog_migrate/f4776db9ba1d72b166251a6a0e39b3c9.png)
结构三:
if(条件表达式1){
执行代码块1;
}else if (条件表达式2){
执行代码块2;
}
……
else{
执行代码块n;
}
流程图如下:
![image-20210402190415422](https://i-blog.csdnimg.cn/blog_migrate/6b5becbed39da574a93010446895ec65.png)
if-else使用说明:
- 条件表达式必须是布尔表达式,即最终结果都必须等价于true或者false
- 当if-else结构是“多选一”时,最后的else是可选的,根据需要可以省略
- 执行代码块只有一条执行语句时,一对{}可以省略,但建议保留,提高代码的可读性
- if-else结构可以嵌套使用。if嵌套时,如果没有{},else 与 if 的配对满足就近原则,即else与最近的一个if配对,比较容易出错
- 关于多选一时条件表达式的声明顺序:
- 当多个条件是“互斥”关系时,条件判断语句及执行语句的声明顺序无所谓
- 当多个条件是“包含”关系时,通常情况下,需要将范围小的声明在范围大的上面。反之,先范围大的,范围小的是没有机会执行的
- 当多个条件是有交集关系时,根据具体的情况,酌情考虑声明的顺序
例子:
/*
由键盘输入三个整数分别存入变量num1、num2、num3,
对它们进行排序(使用if-else结构),并且从小到大输出
*/
public class IfTest01 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入第一个整数:");
int num1 = scanner.nextInt();
System.out.println("请输入第二个整数:");
int num2 = scanner.nextInt();
System.out.println("请输入第三个整数:");
int num3 = scanner.nextInt();
//3个数排序一共6种情况,两层嵌套的话就是每层里面有三个子层
if(num1 >= num2){
if(num3 >= num1){
System.out.println(num2 + "," + num1 + "," + num3);
}else if(num3 <= num2){
System.out.println(num3 + "," + num2 + "," + num1);
}else {
System.out.println(num2 + "," + num3 + "," + num1);
}
}else {
if(num3 >= num2)
System.out.println(num1 + "," + num2 + "," + num3);
else if(num3 <= num1)
System.out.println(num3 + "," + num1 + "," + num2);
else
System.out.println(num1 + "," + num3 + "," + num2);
}
}
}
/*
程序随机地产生一个两位数的彩票,提示用户输入一个两位数,
然后按照下面的规则判定用户是否能赢。
1)如果用户输入的数匹配彩票的实际顺序,奖金10000美元。
2)如果用户输入的所有数字匹配彩票的所有数字,但顺序不一致,奖金3000美元。
3)如果用户输入的一个数字仅满足顺序情况下匹配彩票的一个数字,奖金1000美元。
4)如果用户输入的一个数字仅满足非顺序情况下匹配彩票的一个数字,奖金500美元。
5)如果用户输入的数字没有匹配任何一个数字,则彩票作废。
*/
public class IfTest02 {
public static void main(String[] args) {
// Math.random()产生[0.0,1.0)的数
//两位整数的范围是[10,100)
int randNum = (int)(Math.random()*90 + 10);
//分别取出随机数的个位和十位
int randLow = randNum % 10;
int randHigh = randNum / 10;
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个两位整数:");
int inputNum = scanner.nextInt();
//分别取出输入数据的个位和十位
int inputLow = inputNum % 10;
int inputHigh = inputNum / 10;
//题目中的顺序已经是合理的顺序,依照要求编写即可
//顺序一致,数字全部一致
if(randNum == inputNum){
System.out.println("奖励10000美元");
//顺序不一致,数字全部一致
}else if (randLow == inputHigh && randHigh == inputLow){
System.out.println("奖励3000美元");
//顺序一致,只有一个数字相同
}else if (randHigh == inputHigh || randLow == inputLow){
System.out.println("奖励1000美元");
//顺序不一致,只有一个数字相同
}else if (randHigh == inputLow || randLow == inputHigh){
System.out.println("奖励500美元");
}else {
System.out.println("没有奖励");
}
System.out.println("彩票的号码是:" + randNum);
}
}
switch-case结构
格式如下:
switch(表达式){
case 常量1:
语句1;
// break;
case 常量2:
语句2;
// break;
… …
case 常量N:
语句N;
// break;
default:
语句;
// break;
}
流程图如下:
switch-case使用说明:
-
switch-case的作用:根据switch表达式中的值,依次匹配各个case中的常量。一旦匹配成功,则进入相应case结构中,调用其执行语句。当调用完执行语句以后,则仍然继续向下执行其他case结构中的执行语句,直到遇到break关键字或此switch-case结构末尾结束为止。
-
switch(表达式)中表达式的值 必须是下述几种类型之一:byte ,short,char ,int, 枚举 (jdk5以后更新) ,String (jdk7以后更新)
-
case子句中的值必须是常量,不能是不确定的表达式值或者未赋值的变量, 且同一个switch语句,所有case子句中的常量值互不相同
-
break是可选的,一旦执行到break,则跳出switch-case结构
-
default子句是可选的的。同时,位置也是灵活的。当没有匹配的case时,执行default。相当于if-else中的else
-
因为break可选,所以当多个case的执行语句相同时,可以考虑“合并”。因为case后面的值不同,所以只会执行其中一个,只需在末尾的case中写一个break即可。
来个小例题:
/*
从键盘分别输入年、月、日,判断这一天是当年的第几天,使用switch-case结构完成
注:判断一年是否是闰年的标准:
1)可以被4整除,但不可被100整除
或
2)可以被400整除
*/
public class SwitchTest01 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入year:");
int year = scanner.nextInt();
System.out.println("请输入month:");
int month = scanner.nextInt();
System.out.println("请输入day:");
int day = scanner.nextInt();
//定义一个变量保存天数
int sumDays = 0;
/*这里月份如果不倒序写就会出现:
sumDays = sumDays + 31 + 29 + 31 + ... + 31这种冗余的情况
不写break,根据月份依次加天数
*/
switch(month){
case 12:
sumDays += 31;
case 11:
sumDays += 30;
case 10:
sumDays += 31;
case 9:
sumDays += 30;
case 8:
sumDays += 31;
case 7:
sumDays += 31;
case 6:
sumDays += 30;
case 5:
sumDays += 31;
case 4:
sumDays += 30;
case 3:
sumDays += 31;
case 2:
//判断是否是闰年
if((year%4 == 0 && year%400 != 0) || year%400 == 0){
sumDays += 29;
}else {
sumDays += 28;
}
case 1:
sumDays += day;
}
System.out.println("这一天是"+ year + "的第" + sumDays + "天");
}
}
switch和if语句的对比(应用场景):
- 如果判断的具体数值不多,而且符合byte、short 、char、int、String、枚举等几种类型。虽然两个语句都可以使用,建议使用swtich语句。因为效率稍高。
为啥switch效率比if-else高?
if(a==1){
}
else if(a==2){
}
else if{
}
...
如上代码所示,每次进行if判断时寄存器都要读取一次a的值,寄存器并不知道我们判断的是同一个值。而switch-case语句只需要读取一次。
- 其他情况:对区间判断,对结果为boolean类型判断,使用if,if的使用范围更广。也就是说,使用switch-case的,都可以改写为if-else。反之不成立。
循环结构
所谓循环结构是在某些条件满足的情况下,反复执行特定代码的功能。
有while、do-while、for三种循环语句。
循环语句通常有四个组成部分:
- 初始化部分(init_statement)
- 循环条件部分(test_exp)
- 循环体部分(body_statement)
- 迭代部分(alter_statement)
for循环
语法格式:
for(①初始化部分;②循环条件部分;④迭代部分){
③循环体部分;
}
执行过程:
①-②-③-④-②-③-④-②-③-④-…-②(循环条件不再满足,循环结束)
for循环的说明:
- 循环条件部分为boolean类型表达式,当值为false时,退出循环
- 初始化部分可以声明多个变量,但必须是同一个类型,用逗号分隔;迭代部分可以有多个变量更新,用逗号分隔
- 这里循环语句的四个部分都不是必需的,可以不写,但是容易因少些条件形成死循环,不建议这么做
while循环
语法格式:
①初始化部分
while(②循环条件部分){
③循环体部分;
④迭代部分; //循环体部分和迭代部分顺序可以交换
}
执行过程:
①-②-③-④-②-③-④-②-③-④-…-②
while循环的说明:
- 不要忘记声明④迭代部分。否则,循环将不能结束,变成死循环。
- for循环和while循环可以相互转换,观察执行过程可以发现和for循环是一致的。
- while循环中只有循环条件部分是必须写的
//for与while相互转换,注意迭代条件的顺序
int i = 1;
while (i < 10){
i++;
System.out.println("i=" + i);
}
//**************************************
for (int j = 1; j < 10; ) {
j++;
System.out.println("j=" + j);
}
do-while循环
语法格式:
①初始化部分;
do{
③循环体部分;
④迭代部分; //循环体部分和迭代部分顺序可以交换
}while(②循环条件部分);
执行过程:
①-③-④-②-③-④-②-③-④-…-②
do-while循环的说明:
- do-while循环至少执行一次循环体
- do-while中也只有循环条件部分时必须写的
来个小例题:
输入两个正整数m和n,求其最大公约数和最小公倍数。
public class Test01 {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
System.out.println("请输入正整数m:");
int m = scanner.nextInt();
System.out.println("请输入正整数n:");
int n = scanner.nextInt();
//求最大公约数
//最大公约数的最大值就是m,n中较小的值(m % n==0),所以从大到小开始遍历
for (int i = (m>n) ? n:m; i >= 1 ; i--) {
//是否能除尽
if(m % i == 0 && n % i == 0){
System.out.println("最大公约数i为:" + i);
break;
}
}
//求最小公倍数
//最小公倍数最小值是mn中较大的数,mn存在倍数关系
int j = (m>n) ? m:n;
//最小公倍数的最大值是m*n,mn不存在倍数关系,且没有公约数
while (j <= m*n){
if (j % m == 0 && j % n == 0){
System.out.println("最小公倍数j为:" + j);
break;
}
j++;
}
}
}
循环嵌套
将一个循环放在另一个循环体内,就形成了嵌套循环。其中,for ,while ,do-while均可以作为外层循环或内层循环。
实质上,嵌套循环就是把内层循环当成外层循环的循环体。当只有内层循环的循环条件为false时,才会跳出内层循环,才可结束外层的当次循环,开始下一次的外层循环。
设外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次。
举个简单的应用就是输出九九乘法表:
1*1 = 1
2*1 = 2 2*2 = 4
3*1 = 3 3*2 = 6 3*3 = 9
4*1 = 4 4*2 = 8 4*3 = 12 4*4 = 16
5*1 = 5 5*2 = 10 5*3 = 15 5*4 = 20 5*5 = 25
6*1 = 6 6*2 = 12 6*3 = 18 6*4 = 24 6*5 = 30 6*6 = 36
7*1 = 7 7*2 = 14 7*3 = 21 7*4 = 28 7*5 = 35 7*6 = 42 7*7 = 49
8*1 = 8 8*2 = 16 8*3 = 24 8*4 = 32 8*5 = 40 8*6 = 48 8*7 = 56 8*8 = 64
9*1 = 9 9*2 = 18 9*3 = 27 9*4 = 36 9*5 = 45 9*6 = 54 9*7 = 63 9*8 = 72 9*9 = 81
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i ; j++) {
System.out.print(i + "*" + j + " = " + i*j + " ");
}
System.out.println();
}
以外层循环控制行数,内层循环控制列数
关键字break和continue的使用
关键字 | 使用范围 | 作用 |
---|---|---|
break | switch-case,循环结构 | 终止switch-case,结束当前循环 |
continue | 循环结构 | 结束当次循环 |
这里如何去理解“当前”和“当次”呢?
先体验一下:
for (int i = 1; i <= 7; i++) {
if(i % 4 == 0){
break;
//continue;
//System.out.print(i); 这里的语句并不会得到执行
}
System.out.print(i);
}
如果使用break,输出的结果为:123
如果使用continue,输出结果为:123567
由此可见break是直接结束整个循环语句块,而continue只是跳过其所在循环语句块的一次执行,继续下一次循环
注意: break、continue之后不能有其他的语句(同一对{}之内),因为程序永远不会执行其后的语句。
那么有多层循环时,我们如何跳出指定的循环呢?
可以通过使用一个标签来实现:
label:for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if(j == 2){
System.out.println(j);
break label;
//continue label;
}
}
}
这里就可以直接跳出外层循环了。
内层循环也可以使用:
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
label:if(j == 2){
System.out.println(j);
break label;
//continue label;
}
}
}
此时跳出内层循环,但是一般内层循环没必要使用标签。
这里label的使用相当于:
for (int i = 0; i < 4; i++) {
label:for (int j = 0; j < 4; j++) {
if(j == 2){
System.out.println(j);
break label;
//continue label;
}
}
}
注意:
-
标签只要满足标识符的命名规则即可
-
break和continue都可以使用标签,一般对break使用
-
标签必须紧接在循环语句块的头部,不能对非循环语句块使用
//错误使用 label:int i = 1; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if(j == 2){ System.out.println(j); break label; } } }
总结:
-
三种循环结构的表达方式基本是一致的,没有什么明显的区别
-
几种无限循环的使用方式:while(true),for( ; ; )…
-
循环可以嵌套使用
-
结束循环的方式有break和continue,可以使用标签跳出指定循环
来个典型的题目:
输出100以内的质数(质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数)
简单分析一下:
需要两层循环,外层遍历,内层判断。
判断是否是质数不需要将小于该数的所有数都除一遍,只需要除到该数的开方即可。
public static void printPrimerNumber(){
//定义一个标签,判断是否是质数
boolean flag;
//外层循环遍历2~100的自然数
for (int i = 2; i <= 100; i++) {
flag = true;
//内层循环进行判断是否为质数,在范围内如果就被1以外的数除尽,那么就不是质数,没必要再进行计算,直接判断下一个数
for (int j = 2; j <= Math.sqrt(i); j++) {
if(i % j == 0){
flag = false;
break;
}
}
if (flag){
System.out.println(i);
}
}
}