目录
1.1.if语句:使程序能够基于某种条件有选择地执行某些语句。
1.2.switch语句:基于一个表达式的值决定要执行的一组语句。
2.1.while语句:使一个语句块在某种条件为真时循环执行,执行前不知循环次数。
在结构化程序设计中规定三种基本流程结构:顺序结构(比较简单,按照代码的书写顺序一行一行执行,如果调整代码的书写顺序,则执行顺序也发生变化)、分支结构、循环结构。
程序流控制语句可以使程序运行时,有条件地执行或重复执行某些语句,改变程序正规的顺序执行流向。Java语言提供了4类程序流控制语句。
- 分支语句
- 循环语句
- 跳转语句
- 异常处理语句
1.分支语句
1.1.if语句:使程序能够基于某种条件有选择地执行某些语句。
基本语法形式1:
if(布尔表达式){
//条件满足时执行代码
}
基本语法形式2:
if(布尔表达式){
//条件满足时执行代码
}else{
//条件不满足时执行代码
}
基本语法形式3:多分支的情况
if(布尔表达式){
//条件满足时执行代码
}else if(布尔表达式){
//条件满足时执行代码
}else{
//条件都不满足时执行代码
}
注:
1).区别:
if( ){ }
if( ){ }
是两个可能都走。
if( ){ }
else if( ){ }
要么走上要么走下。
2).条件运算符"? : "是if else语句的一种紧缩格式的表达。
3).悬垂else问题
int x = 10;
int y = 10;
if (x == 10)
if (y == 10)
System.out.println("aaa");
else
System.out.println("bbb");
//运行结果
aaa
if else语句中不加大括号也可以写语句(只能写一条语句)。此时else是和最接近的if匹配(就近原则)。但是实际开发中不建议这么写,最好加上大括号。
4).代码风格问题
// 风格1
int x = 10;
if (x == 10) {
// 满足条件
} else {
// 不满足条件
}
// 风格2
int x = 10;
if (x == 10)
{
// 满足条件
}
else
{
// 不满足条件
}
虽然两种方式都是合法的,但是 Java 中更推荐使用风格1,{ 放在 if else 同一行。
5).分号问题
int x = 20;
if (x == 10); {
System.out.println("hehe");
}
// 运行结果
hehe
此处多写了一个分号, 导致分号成为了 if 语句的语句体, 而 { } 中的代码已经成为了一个和 if 无关的代码块。
1.2.switch语句:基于一个表达式的值决定要执行的一组语句。
基本语法:
switch(整数|枚举|字符|字符串){
case 内容1 :
内容满足时执行语句(组);
break;
case 内容2 :
内容满足时执行语句(组);
break;
...
[default:
内容都不满足时执行语句(组);
break;]
}
根据 switch 中值的不同,会执行对应的 case 语句。遇到 break 就会结束该 case 语句。
如果 switch 中的值没有匹配的 case,就会执行 default 中的语句。我们建议一个 switch 语句最好都要带上 default(default子句是可选的,并且最后一个break语句可省略)。
注:
1).switch语句中各case分支既可以是单条语句,也可以是由多条语句组成的语句组,该语句组可以不用{ }括起来。
2).break不要遗漏,否则会失去"多分支选择"的效果。
int day = 1;
switch(day) {
case 1:
System.out.println("星期一");
// break;
case 2:
System.out.println("星期二");
break;
}
// 运行结果
星期一
星期二
发现不写 break 的时候, case 语句会依次向下执行, 从而失去了多分支的效果。
switch在每个case后面要加上break表示终止;若没有break终止,当switch遇到满足条件的分支后,会一直执行到switch最后。
3).switch中的值只能是 整数|枚举|字符|字符串。
double num = 1.0;
switch(num) {
case 1.0:
System.out.println("hehe");
break;
case 2.0:
System.out.println("haha");
break;
}
// 编译出错
Test.java:4: 错误: 不兼容的类型: 从double转换到int可能会有损失
4).switch不能表达复杂的条件。
// 例如: 如果 num 的值在 10 到 20 之间, 就打印 hehe
// 这样的代码使用 if 很容易表达, 但是使用 switch 就无法表示.
if (num > 10 && num < 20) {
System.out.println("hehe");
}
5).switch 虽然支持嵌套, 但是很丑。
int x = 1;
int y = 1;
switch(x) {
case 1:
switch(y) {
case 1:
System.out.println("hehe");
break;
}
break;
case 2:
System.out.println("haha");
break;
}
代码的美观程度也是一个重要的标准,毕竟这是看脸的世界。
综上发现,switch 的使用局限性是比较大的。
PS:switch语句扩展与switch表达式
Java SE 12和Java SE 13对switch进行了扩展,解决了上述问题。
switch的扩展,主要体现在case分支的定义,有两种形式:
a.传统的"case... : "分支(冒号case)
b.新的"case... -> "分支(箭头case)
case label_1, label_2, ... ,label_n -> expression; | throw - statement; | block
上述定义中:
1).允许在一个case分支里出现以逗号分隔的多个常量值。程序运行时,这些值中的任何一个匹配成功,箭头右侧的代码都将被运行,并且在这些代码结束后,将不会运行switch表达式或语句中任何其他分支中的代码。因此,箭头case解决了传统冒号case的控制流贯穿问题。
2).箭头右侧的代码可以是一个表达式,一个throw语句或一个语句块(包含多个语句)。
在语句块中声明的局部遍历,其作用域只是所在的语句块而不是整个switch语句。
如果箭头右侧是一个表达式,则该表达式的值将是switch表达式的值。
箭头case的switch语句:
int numLetters = 0; Day day = Day.WEDNESDAY; switch(day){ case MONDAY, FRIDAY, SUNDAY -> numLetters = 6; case TUESDAY -> numLetters = 7; case THURSDAY, SATURDAY -> numLetters = 8; case WEDNESDAY -> numLetters = 9; default -> throw new IllegalStateException("Invalid day: " + day); }; System.out.println(numLetters);
箭头case的switch表达式:
Day day = Day.WEDNESDAY; System.out.println( switch(day){ case MONDAY, FRIDAY, SUNDAY -> 6; case TUESDAY -> 7; case THURSDAY, SATURDAY -> 8; case WEDNESDAY -> 9; default -> throw new IllegalStateException("Invalid day: " + day); } );
Java SE 13中引入了yield语句,定义为:
yield someValue
其中someValue是yield所在case分支产生的switch表达式的值。
int j = switch(day){ case MONDAY -> 0; case TUESDAY -> 1; default -> { int k = day.toString().length(); int result = f(k); yield result; } };
break和yield语句使得switch语句和switch表达式易于区分:
yield语句用于switch表达式,break语句用于switch语句。
传统的冒号case可以使用yield语句构造switch表达式。
int result = switch(s){ case "Foo": yield 1; case "Bar": yield 2; default: System.out.println("Neither Foo nor Bar, hmmm..."); yield 0; };
2.循环语句
2.1.while语句:使一个语句块在某种条件为真时循环执行,执行前不知循环次数。
基本语法
while(循环条件){
循环语句;
}
循环条件(逻辑表达式)为 true,则执行循环语句;否则false结束循环。
注:
1).和 if 类似, while 下面的语句可以不写 { },(只支持一条语句),建议加上 { }。
2).和 if 类似, while 后面的 { 建议和 while 写在同一行。
3).和 if 类似, while 后面不要多写分号,否则可能导致循环不能正确执行。
int num = 1;
while (num <= 10); {
System.out.println(num);
num++;
}
// 执行结果
[无任何输出, 程序死循环]
此时 ; 为 while 的语句体(这是一个空语句),实际的 { } 部分和循环无关。此时循环条件 num <= 10 恒成立,导致代码死循环了。
2.2.do while语句:执行前不知循环次数。
基本语法:
do{
循环语句;
}while(循环条件);
先执行循环语句,再判定循环条件。至少把循环体中的语句执行一遍。
注:
1).do while 循环最后的分号不要忘记。
2).一般 do while 很少用到,更推荐使用 for 和 while。
2.3.for语句:循环执行的次数是可以在执行前确定的。
基本语法:
for(初始语句;逻辑表达式;迭代语句){
语句或语句块;
}
- 初始语句: 用于初始化循环变量。
- 逻辑表达式: 循环条件。
- 迭代语句: 更新循环变量。
for语句执行时先执行初始语句,判断逻辑表达式的值,当逻辑表达式为true时,执行循环体语句,接着执行迭代语句,然后再去判断逻辑表达式的值。这个过程一直进行下去,直到逻辑表达式的值为false,循环结束并转到for之后的语句。
注:(和while循环类似)
1).和 if 类似, for 下面的语句可以不写 { } ,(只支持一条语句),建议加上 { }。
2).和 if 类似, for 后面的 { 建议和 for写在同一行。
3).和 if 类似, for 后面不要多写分号, 否则可能导致循环不能正确执行。
4).可以在for循环的初始化部分声明一个变量,它的作用域为整个for循环。
5).for循环通常用于循环次数确定的情况,但也可以根据循环结束条件完成循环次数不确定的情况。
6).在初始化部分和迭代部分可以使用逗号语句来进行多个操作。逗号语句是用逗号分隔的语句序列。例如:
for(int i = 0, j = 10; i < j; i++, j--){
...
}
7).初始化,终止以及迭代部分都可以为空语句(但分号不能省略),逻辑表达式为空时,默认表达式为恒真。可以用下列for循环表示无限循环:
for( ; ; ){
...
}
8).for循环常用于数组各个元素或一个字符串中各个字符的处理。
PS:for-each语句
- for-each语句是for语句的增强版,简化了代码,提高了代码的可读性和安全性(数组越界)。但for-each并不能替代for循环。任何for-each都能改写为for循环,反之不行。
- for-each语句可用于依次遍历数组或集合中的每一个元素,而不必考虑指定下标值(不考虑从哪开始,从哪结束,不考虑数组越界问题)。如果不想遍历整个数组或集合或者在循环内部需要操作下标值,就要用传统的for循环。
- for-each语句只可遍历访问输出数组或集合,无法在遍历的过程中对数组或集合进行修改,而for循环可以在遍历的过程中对原数组或集合进行修改。
public class ArrayLearn { public static void main(String[] args) { int[] arr = {1, 2, 3}; for (int i : arr) { if(i == 1) { i = 9; } } System.out.println(arr[1]); } } //打印2 //改的是临时变量的值,不是数组元素的值
语法格式:
for(元素类型t 元素变量x : 遍历对象A(数组或集合)){ 引用元素变量x的语句; }
每走一次循环,就将数组的元素依次赋值给临时变量x。
遍历数组:
public static void main(String[] args) { String[] demo = {"a", "b", "c"}; for (String t :demo ) { System.out.println(t); } }
遍历list集合:
public static void main(String[] args) { List<String> list = new ArrayList(); list.add("a"); list.add("b"); list.add("c"); for(String str : list){ System.out.println(str); } }
3.循环跳转语句:break和continue
break[label]; //用来从switch语句或循环语句中跳出
continue[label]; //跳过循环体的剩余语句,开始执行下一次循环
这两个语句都可以带标签(label)使用,也可以不带标签使用。
标签是出现在一个语句之前的标识符,标签后面要跟上一个冒号(:),标签定义如下:
label: statement;
3.1.break语句:
让本次循环提前结束(若有多层循环嵌套,就近跳出)。
代码示例:找到100-200中第一个3的倍数
int num = 100;
while(num <= 200){
if(num % 3 == 0){
System.out.println("找到了3的倍数,为:" + num);
break;
}
num++;
}
//执行结果
找到了3的倍数,为:102
3.1.1.带标签
带标签的break语句将结束标签所指示的循环的执行。如:它结束了switch语句的执行,并把控制流转移到紧跟在switch之后的语句。
3.1.2.不带标签
不带标签的break语句可以终止循环,用来结束最内层的switch,for,while和do while语句的执行。
3.2.continue语句:
在for,while和do while循环中,continue语句跳过当前循环的其余语句,执行下一次循环,当然执行下次循环前要判定循环条件是否满足。
代码示例:找到100-200中所有3的倍数
int num = 100;
while(num <= 200){
if(num % 3 != 0){
num++;
continue;
}
System.out.println("找到了3的倍数,为:" + num);
num++;
}
3.2.1.带标签
带标签的continue语句结束由标签所指外层循环的当前循环,开始执行该循环的下次循环。
3.2.2.不带标签
不带标签的continue语句跳过最内层的循环,并开始执行最内层循环的下一次循环。
PS:return语句
- 用return表示当前方法结束调用,当前方法里的后面代码不再执行,有返回值的情况下返回相应的值。继续执行当前方法之后的语句。
- 在返回类型为void的方法里面,有个隐含的return语句,return可以省略不写。
4.异常处理语句
4.1.捕获异常:try catch finally语句。
4.2.抛出异常:throw语句。
(在异常文章中有详细讲解)