1、顺序结构
任何编程语言中,最常见的程序结构就是顺序结构。
顺序结构指程序从上到下逐行地执行,中间没有任何判断和跳转。
在 main 方法中多行代码之间没有任何流程控制,则程序总是从上向下一次执行,排在前面的代码先执行,排在后面的代码后执行。
2、分支结构
Java 提供了两种常见的分支控制结构:
if
switch
if 语句使用布尔表达式或布尔值作为分支条件进行分支控制;而 switch 语句则用于对多个整型值(Java 7 支持String类型)进行匹配,从而实现分支控制。
2.1 if 条件语句
if 语句有下列三种形式。
第一种:
if (条件表达式){ // 逻辑代码 }
第二种:
if (条件表达式){ // 逻辑代码 }else { // 逻辑代码 }
第三种:
if (条件表达式1){ // 逻辑代码 }else if (条件表达式2){ // 逻辑代码 }else if (条件表达式3){ // 逻辑代码 } ...省略多个 else if 语句 else { // 逻辑代码 }
三种类型类型中,条件表达式的返回值只能是 true 或者 false。
在上面的条件语句中,if (表达式) 、else if (表达式) 和 else 后花括号括起来的多行代码被称为代码块,一个代码块通常被当成一个成体来执行(除非运行过程中遇到 return、break、continue 等关键字,或者遇到了异常),因此这个代码块也被称为条件执行体。例如如下代码。
public class BranchTest { public static void main(String[] args) { int age = 18; if (age == 18){ // 只有当 age 等于 18 时下面的两个输出语句才会执行 System.out.println("噢噢噢!原来 J3 才 18 岁。"); System.out.println("不愧是靓仔"); } System.out.println("一个毫无意义的输出,就像 J3 还认为自己 18 岁。"); } }
如果 if(表达式)、else if(表达式)和 else 后的代码只有一行语句是,则可以省略花括号,因为单行语句本身就是一个整体,无须用括号来把它们定义成一个整体。例如下面代码完全可以执行。
public class BranchTest { public static void main(String[] args) { int age = 17; if (age > 18) System.out.println("J3 老是拿年龄做文章,都不敢正视自己的年纪!!!"); else System.out.println("嘿嘿!我要让上面的语句不能输出,敢质疑我,哼!"); } }
但,通常我们是不建议这样做的,即时条件执行体只有一条语句,也要保留花括号,因为可读性较好,而且这会减少错误发生的可能。
例如下面代码和想象的结果可能有不同。
public class BranchTest { public static void main(String[] args) { int age = 19; if (age > 18) System.out.println("J3 老是那年龄做文章,都不敢正视自己的年纪!!!"); else System.out.println("嘿嘿!我要让上面的语句不能输出,敢质疑我,哼!"); // 这个输出永远会执行 System.out.println("J3 永远是 18"); } }
System.out.println("J3 永远是 18")
这个语句永远会执行的原因就是它不是 else 后的条件执行体。
如果,if 后面有多条语句作为条件执行体,若省略了这个条件执行体的花括号,则会引起编译错误。例如下面代码。
public class BranchTest { public static void main(String[] args) { int age = 19; if (age > 18) System.out.println("J3 老是那年龄做文章,都不敢正视自己的年纪!!!"); System.out.println("这个输出写在这,是会报错的在没有花括号的时候!!!!!"); else System.out.println("嘿嘿!我要让上面的语句不能输出,敢质疑我,哼!"); // 这个输出永远会执行 System.out.println("J3 永远是 18"); } }
2.2 switch分支语句
switch 语句由一个控制表达式和多个 case 标签组成,和 if 语句不同的是,switch 语句后面的控制表达式的数据类型只能是 byte、short、char、int 四个整数类型和枚举类型,不能是 boolean 类型。
Java 7 改进了 switch 分支语句,Java 7 允许 switch 语句中的控制表达式为 java.lang.String 类型。
switch 语句往往需要在 case 标签后紧跟着一个代码块,case 标签作为这个代码块的标识。语法格式如下。
public class BranchTest { public static void main(String[] args) { switch (表达式){ case 情况1:{ // 逻辑1 break; } case 情况2:{ // 逻辑2 break; } ... default:{// 都不满足,则默认执行这个 // 默认逻辑 } } } }
这种分支语句的执行先是对表达式求值,然后依次匹配情况1、情况2、…等值,遇到匹配的值及执行对应的执行体;如果所有 case 标签后的值都不与表达式的值相等,则执行 default 标签后的代码块。
和 if 语句不同的是,switch 语句中各个 case 标签后代码块的开始点和结束点非常清晰,因此完全可以省略 case 后面的花括号。
与 if 语句中的 else 类似,当 switch 表达式中的值没有任何一种情况匹配时,就会执行 default 后面的代码块。
switch 使用案例如下。
public class BranchTest { public static void main(String[] args) { char name = 'J'; switch (name){ case 'A':{ System.out.println("name 等于 A"); break; } case 'B':{ System.out.println("name 等于 B"); break; } case 'J':{ System.out.println("name 等于 J"); break; } default:{ System.out.println("name 啥也不是!"); } } } }
运行结果输出了 “name 等于 J” 符合我们的预期。
看代码我们发现,每个 case 标签后面都有一条 break;语句,这个 break;语句还是挺重要的,如果把上面代码的 break;都去掉那么结果会是如下情况。
name 等于 J name 啥也不是!
看起来有点奇怪,但这就是由 switch 语句的运行流程决定的。
switch 语句会先求出表达式的值,然后和 case 标签后的值进行比较,一旦遇到相等的值,就开始执行 cases 标签后面的代码,并不在判断后面 case、default 标签的条件是否匹配,除非是遇到 break;才会结束。
Java 7 增强了 switch 语句的功能,允许 switch 语句的控制表达式是 java.lang.String 类型的变量或者表达式(只允许了 java.lang.String 其他类型字符串不行)。例如如下代码。
public class BranchTest { public static void main(String[] args) { String blogType = "CSDN"; switch (blogType){ case "CSDN":{ System.out.println("J3,CSDN老牌用户,新人作者!"); break; } case "juejin":{ System.out.println("J3,掘金新用户,新人作者"); break; } case "zhihu":{ System.out.println("J3,知乎新用户,新人作者"); break; } default:{ System.out.println("J3,啥也不是!"); } } } }
在使用 switch 时,有两个地方值得注意:
switch 表达式类型只能是 byte、short、char、int、String。
如果 case 标签后的代码省略了 break;则会一直往下执行直到碰到 break;。
3、循环结构
在循环语句中,满足循环条件的情况下,可以反复执行一段代码,重复执行的代码称为循环体。
循环结构需要注意的是,循环条件。当循环条件永远为 true 时会造成死循环。
3.1 while 循环语句
while 循环的语法格式如下:
while (表达式或布尔值) { // 循环体代码 }
在执行 while 循环体之前,先对表达式求值,如果条件为 true,则运行循环体部分。
下面演示一个简单的 while 循环。
public class CycleTest { public static void main(String[] args) { int i = 1; int sum = 0; while (i <= 10) { // 循环,求 1 到 10 的和。 sum = sum + i; i = i + 1; } System.out.println("1 到 10 的累加和:" + sum); } }
值得注意的是 i <= 10 这个表达式的结果要在某个时刻变为 false ,不然程序会进行死循环,造成程序永远无法结束。
3.2 do while 循环语句
do while 循环与 while 循环的区别在于:while 循环是先判断循环条件,如果条件为真则执行循环体;而 do while 循环则先执行循环体,然后才判断循环条件,如果循环条件为真,则执行下一次循环,否则终止循环。
do while 语法格式如下。
do { 循环体 }while (表达式);
注意,while 的后面是要跟一个分号,表明循环结束。
下面看看简单用法。
public class CycleTest { public static void main(String[] args) { int age = 16; do { System.out.println("时间,一年年的过去,转眼我 J3 已经:" + age + "岁了。"); age = age + 1; } while (age < 25); System.out.println("转眼我都这么大了~"); } }
由程序我们可以发现,do while 循环不管表达式条件真假,都会执行一遍循环体。
3.3 for 循环
for 循环是一个更加简洁的循环语句,大部分情况下,for 循环可以替代 while 循环、do while 循环。
for 循环的基本语法格式如下。
for (初始化值; 循环条件 ; 迭代语句) { 循环体 }
程序执行 for 循环时,先执行循环的初始化值,初始化值语句只在循环开始前执行一次。
每次执行循环体之前,先判断循环条件的真假,true 执行循环体,执行循环体完成之后再执行迭代语句。
因为是先判断循环条件真假,在执行循环体,所以循环条件总要比循环体多执行一遍。
简单使用案例如下。
public class CycleTest { public static void main(String[] args) { int sum = 0; for (int i = 1; i <= 10; i++) { sum = sum + i; System.out.println("我被执行了第 " + i + " 次。"); } System.out.println("1 到 10 的累加和:" + sum); } }
当然,对于初始化语句是允许指定多个初始语句,循环条件也是可以包含逻辑运算表达式。程序如下。
public class CycleTest { public static void main(String[] args) { for (int i = 0, b = 2, c = 3; i <= 7 && b < 8 && c < 9; i++) { System.out.println("我执行了~~~~~~~"); } } }
上面代码中初始化变量有三个,但只能有一个声明语句,因此如果需要在初始化表达式中声明多个变量,那么这些变量应该具有相同的数据类型。
在 for 循环括号中只有两个分号是必须的,初始化语句、循环条件、迭代语句部分都是可以省略的,如果省略了循环条件,则这个循环条件模式 true ,将会产生一个死循环。例如下面程序。
public class CycleTest { public static void main(String[] args) { for (; ; ) { System.out.println("我将要进行死循环输出了~~~"); } } }
上面代码将进行死循环,一直输出 我将要进行死循环输出了~~~
字符串。
使用 for 循环还是以写成如下形式。
public class CycleTest { public static void main(String[] args) { // 初始语句 int i = 1; for (; i <= 10 ; ) { System.out.println("我是变种 for 循环!"); // 迭代语句 i = i +2; } } }
这种写法与 while 的形式很类似,并且这种写法的初始值 i 即时在 for 循环体外也是可以访问的,而初始化语句写在括号里面的话,则只能在循环体中使用(变量作用范围,后面介绍)。
3.4 嵌套循环
如果把一个循环放在另一个循环体内,那么就可以形成嵌套循环了,嵌套循环既可以是 for 循环嵌套 while 循环,也可以是 while 循环嵌套 do while 循环…既各种类型的循环都可以作为外层循环,各种类型的循环也可以作为内层循环。
当外层循环的循环次数为 n 次,内层循环的循环次数为 m 次,那么内层循环的循环体实际上需要执行 n * m 次。
嵌套循环执行流程如下图。
从流程图中我们可以看出,嵌套循环就是把内层循环当成外层循环的循环体。
当只有内层循环的循环条件为 false 时,才会完全跳出内层循环,才可以结束外层循环的当次循环,开始下一次循环,下面看个简单案例。
public class NestedTest { public static void main(String[] args) { for (int i = 1; i <= 9; i++) { for (int j = 1; j <= i; j++) { System.out.print( j + " * " + i + " = " + i * j + " "); } System.out.println(""); } } }
程序执行结果是一个九九乘法表,通过前面知识积累,相信理解起来不费劲。
实际上,嵌套循环不仅可以嵌套两层,只要你愿意,多少层都是可以的。但不管如何嵌套循环,我们都可以吧内层循环当成外层循环体来对待,区别只是这个循环体包含了需要反复执行的代码。
4、控制循环结构
Java 语言没有提供 goto 语句来控制程序的跳转,这种做法提高了流程控制的可读性,但降低了程程控制的灵活性。
为了弥补这种不足, Java 提供了 continue 和 break 来控制循环结构。除此之外,return 可以结束整个方法,当然也就是结束一次循环了。
4.1 break 结束循环
循环的时候我们不一定要等到循环条件为 false 的时候才退出循环,使用 break 就可以强行终止循环。
在循环中,一旦循环体中遇到 break ,系统将完全结束该循环,开始执行循环之后的代码。例如下面程序。
public class NestedTest { public static void main(String[] args) { for (int i = 1; i <= 10000; i++) { System.out.println("我在循环 一万次!"); if (i == 10){ System.out.println("让你执行 1 万次,疯了,不行,我要结束你!"); break; } } System.out.println("结束了"); } }
break 语句不仅可以结束其所在的循环,还可以直接结束外层循环。此时需要在 break 后紧跟一个标签,这个标签用于表示一个外层循环。
Java 中的标签就是一个紧跟着英文冒号(:)的标识符。与其他语言不同的是,Java 中的标签只有放在循环语句之前才有作用。例如下面代码。
public class NestedTest { public static void main(String[] args) { // 外层循环,outo 作为标识符 outo: for (int i = 1; i <= 100; i++) { for (int j = 1; j <= 100; j++) { System.out.println("我又在循环 1 万次!"); if ( (i * j) > 10){ System.out.println("给你脸了,又来循环 1 万次,给我全部结束,不要执行了!"); // 跳出 outo 标签所在标识的循环 break outo; } } } System.out.println("我结束了!"); } }
程序从外层循环进入内层循环后,当 i * j > 10 后,程序遇到一个 break outo;语句,这行代码将会导致结束 outo 标签指定的循环,不是结束 break 所在的循环,而是结束 break 玄幻的外层循环。
通常紧跟 break 之后的标签,必须在 break 所在的循环的外层循环之前定义才有意义。
4.2 continue 结束本次循环
continue 的功能和 break 有点类似,区别是 continue 只是终止本次循环,接着开始下次循环;而 break 则是完全终止循环本身。可以理解为 continue 的作用是跳过当次循环中剩下的语句,重新开始一次新的循环。例如下面代码。
public class NestedTest { public static void main(String[] args) { for (int i = 0; i < 10; i++) { System.out.println("我是 i = " + i + " 前面的语句"); if (i <= 3){ continue; } System.out.println("我是 i = " + i + " 后面的语句"); } } }
当 i 小于等于 3 的时候 if 下面的语句没有输出,因为程序走了 if 体里面的 continue ,忽略了当次循环,开始了新循环。
与 break 类似的是,continue 后也可以紧跟一个标签,用于直接跳过标签所在标识循环的当次循环的剩下语句,重新开始下一次循环。例如下面代码。
public class NestedTest { public static void main(String[] args) { outo: for (int i = 1; i < 10; i++) { for (int j = 1; j < 5; j++) { System.out.println("这人又来 for 循环了~~~"); if (i * j > 8) { System.out.println("只能执行到这了,下面的就先跳过吧,不用执行了"); continue outo; } System.out.println("内层循环,我是否能被执行到!!!"); } System.out.println("======外层循环,我是否能被执行到!!!"); } } }
当程序执行到 continue outo 语句时,直接跳过了内层循环和外层循环,执行了外层的下一次循环。
与 break 类似的是,continue 后的标签也必须是一个有效标签,即这个标签通常应该放在 continue 所在循环的外层循环之前定义。
4.3 return 结束方法
return 关键字并不是专门用于结束循环的,return 的功能是结束一个方法。当一个方法执行到 return 语句时(return 关键字后还可以跟变量、常量和表达式,后面会介绍),这个方法将被结束。
例如下面代码会结束改方法。
public class NestedTest { public static void main(String[] args) { for (int i = 0; i < 100000; i++) { System.out.println("我又在不停的循环中..."); if (i > 10){ System.out.println("过分,1 万都不让你循环,你还循环 10 次,做梦,我要终结整个方法。"); return; } } } }
上面代码,碰到 return 语句后,程序立马结束,不管循环多少次或多少层。
好了,今天的内容到这里就结束了,关注我,我们下期见