一、引言

Java程序流程控制是构建程序逻辑的核心机制,包含顺序、分支、循环三大基础结构。

  • 顺序结构是默认执行方式,代码按书写顺序逐行运行,为复杂逻辑提供基础框架。

  • 分支结构通过条件判断实现路径选择,包括单分支(if)、双分支(if-else)及多分支(if else if ...switch-case)。其中,switch-case语句支持等值匹配。

  • 循环结构则通过forwhiledo-while实现重复操作:for适用于已知次数的遍历,while先判断后执行,而do-while确保至少执行一次循环体。

Java 14引入的箭头语法(case A ->),既优化了代码可读性,也避免了传统case穿透问题。

此外,流程控制中还可以配合跳转语句(如break跳出循环、continue跳过当前迭代、return终止方法)增强灵活性。

使用Java程序流程控制构建高效、易维护的逻辑,以满足从简单数据判断到复杂算法实现的多样化需求。

二、流程控制的基础结构

1. 顺序结构

Java程序中的顺序结构是流程控制中最基础且不可或缺的逻辑框架,其核心特征在于代码按照书写顺序逐行执行,形成线性且不可逆的执行路径。顺序结构具有以下特征:

  • ​线性执行逻辑

程序从入口点(如main方法)开始,逐行向下执行每一条语句,直到所有代码执行完毕或遇到程序终止指令。

  • ​无条件执行​

顺序结构中的所有语句均会被执行,不受任何条件限制。即使后续代码中存在分支或循环,整体流程仍以顺序结构为框架。

  • ​代码可预测性​

由于执行路径固定,顺序结构的程序行为预测,便于调试和维护。

下面的示例展示了Java程序中的顺序结构。

public class SequentialStructure {

	public static void main(String[] args) {
		int a = 10; // 声明变量a并赋值
		int b = 20; // 声明变量b并赋值
		int sum = a + b; // 计算a+b的和,并存储结果
		System.out.println(sum); // 输出结果
	}
}

程序执行后的结果如下:

30

上面的程序严格按照声明并赋值→计算→输出的顺序执行,直到程序运行结束,体现了典型的顺序结构。

对于无需条件判断或循环的算法(如数学公式计算),顺序结构可直接实现。在用户交互场景中,顺序结构也常用于依次接收输入、处理数据并返回结果。即使程序中包含分支结构或循环结构,程序的整体流程仍以顺序结构为主体。

2. 分支结构

在Java编程中,选择结构是控制程序执行流程的重要手段,它允许程序根据条件选择不同的执行路径。Java提供了几种主要的选择结构:包括单分支(if)、双分支(if-else)及多分支(if else if ...switch-case)。

2.1 if选择语句

if选择语句是最基本的程序流程控制语句,用于根据条件表达式的结果来决定是否执行特定的代码块。语法如下所示:

if(条件表达式){
   statement
}

其中条件表达式必须返回一个具体的布尔值,如果返回true值,则执行{statement}中的代码;如果返回false,则结束if语句,执行{statement}之后的代码。

虽然Java语法允许在{statement}中的代码仅有一行时,可以省略大括号{},但通常建议不要省略,以避免逻辑错误。

下面的示例展示了if语句的用法。

public class IfStatement {

	public static void main(String[] args) {
		int a = 0, b = 0;
		if (a == b) {
			System.out.println("a和b的值相等。");
		}
		System.out.println("if语句执行完毕。");
	}
}

程序执行后的结果如下:

a和b的值相等。
if语句执行完毕。

上面的程序首先声明a、b两个变量并赋值0,然后通过if语句判断a == b的返回值,当返回值为true时,执行if之后{}内的语句。最后执行if语句之后的语句。

2.2 if…else…选择语句

if…else…选择语句在if选择语句的基础上增加了else子句,用于根据条件表达式的结果来决定是否执行特定的代码块。语法如下所示:

if(条件表达式){
   statement-1
}
else{
   statement-2
}

其中条件表达式必须返回一个具体的布尔值,如果返回true值,则执行{statement-1}中的代码;如果返回false,则执行{statement-2}中的代码。

下面的示例展示了if…else…语句的用法。

public class IfElseStatement {

	public static void main(String[] args) {
		int a = 10, b = 20;
		if (a > b) {
			System.out.println("a和b相比较,a的值较大。");
		} else {
			System.out.println("a和b相比较,a的值较小。");
		}
		System.out.println("if…else…语句执行完毕。");
	}
}

程序执行后的结果如下:

a和b相比较,a的值较小。
if…else…语句执行完毕。

上面的程序首先声明a、b两个变量并分别赋值10、20,然后通过if语句判断a > b的返回值,当返回值为true时,执行if之后{}内的语句;当返回值为false时,执行else之后{}内的语句。最后执行if…else…语句之后的语句。

2.3 if…else if…else…选择语句

if…else if…else…选择语句实际上是一种if…else…选择语句的嵌套形式,它将if…else…选择语句嵌套到上一层if…else…选择语句的else子句中,形成一种多分支的选择结构。语法如下所示:

if(条件表达式-1){
   statement-1
}
else if(条件表达式-2){
   statement-2
}
else if(条件表达式-3){
   statement-3
} 
……//省略若干分支条件
else {
   statement-n
}

if…else if…else…选择语句从上到下执行,依次判断每个if的条件表达式,当某个条件表达式为true时,就执行与之关联的if之后{}内的语句,并略过剩余的语句执行if…else if…else…选择语句之后的语句。如果所有的条件表达式都不成立,就执行最后的else语句。

因为if语句可以独立执行,如果最后嵌套的if…else…选择语句省略了else子句,那么整个if…else if…else…选择语句的条件表达式不成立时,就直接跳过,执行其后的语句。

下面的示例展示了if…else if…else…语句的用法。

public class NestedIfElseStatement {

	public static void main(String[] args) {
		int month = 5;
		if (month == 12 || month == 1 || month == 2) {
			System.out.println("寒冷的冬天");
		} else if (month >= 3 && month <= 5) {
			System.out.println("温暖的春天");
		} else if (month == 6 || month == 7 || month == 8) {
			System.out.println("炎热的夏天");
		} else if (month >= 9 && month <= 11) {
			System.out.println("凉爽的秋天");
		} else {
			System.out.println("无效的月份");
		}
	}
}

程序执行后的结果如下:

温暖的春天

上面的程序使用if…else if…else…选择语句,在多个条件表达式中对month变量的数据进行判断,根据month变量保存的数据情况确定需要执行的语句。在这段代码中,不管month保存了哪个int取值范围内的数值,都会有一条与其条件对应的语句执行。

2.4 switch-case选择语句

switch-case选择语句是Java提供的另一种多分支选择结构的语句,它根据表达式计算结果的定值执行不同的分支语句,形成一种多分支的选择结构。语法如下所示:

switch(表达式){
   case value1 :
      statement-1
      break; 
   case value2 :
      statement-2
      break; 
   ……//省略若干分支条件
   default : //可选
      statement-n
}

switch后面的圆括号中的表达式必须能计算出结果,在JDK 1.7以前的版本中,该结果的数据类型只能是byte、short、int、char或枚举,从JDK 1.7开始增加支持String类型。switch后面{}中包含若干个case分支,每个case后面的都有一个常量或者字面量的定值,该定值用于和表达式的计算结果比较,如果两个值相同,就执行该值冒号之后的Java语句,直到遇到break跳出整个switch。在所有case分支之后还有一个可选的default分支,用于处理所有 case均不匹配的情况。

注意:case分支内的break可以省略,省略break会发生case穿透,即执行后续case冒号之后的Java语句直至遇到break或switch结构结束。

下面的示例展示了switch-case选择语句的用法。

public class SwitchStatement {

	public static void main(String[] args) {
		int month = 5;
		switch (month) {
		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:
			System.out.println("炎热的夏天");
			break;
		case 7:
			System.out.println("炎热的夏天");
			break;
		case 8:
			System.out.println("炎热的夏天");
			break;
		case 9:
			System.out.println("凉爽的秋天");
			break;
		case 10:
			System.out.println("凉爽的秋天");
			break;
		case 11:
			System.out.println("凉爽的秋天");
			break;
		case 12:
			System.out.println("寒冷的冬天");
			break;
		default:
			System.out.println("输入错误");
		}
	}
}

程序执行后的结果如下:

温暖的春天

上面的程序使用switch-case选择语句,在多个case之后使用1-12的整型数据匹配month变量中保存的整型数据,当case匹配到5时,执行冒号之后的语句输出温暖的春天,然后执行break跳出switch-case选择语句。由于一年有12个月份,所以上面的switch-case选择语句使用了12个case,再加上最后的default,使得代码冗长,降低阅读性。一年的12个月份对应4个季节,利用case穿透机制可以将上面的程序代码优化为:

public class SwitchStatementCaseThought {

	public static void main(String[] args) {
		int month = 5;
		switch (month) {
		case 1:	case 2: case 12:
			System.out.println("寒冷的冬天");
			break;
		case 3: case 4: case 5:
			System.out.println("温暖的春天");
			break;
		case 6: case 7: case 8:
			System.out.println("炎热的夏天");
			break;
		case 9: case 10: case 11:
			System.out.println("凉爽的秋天");
			break;
		default:
			System.out.println("输入错误");
		}
	}
}

程序执行后的结果如下:

温暖的春天

3. 循环结构

Java的循环结构主要包括while语句、do…while语句和for语句,其中for语句包含两种形式,传统形式和for-each增强形式。循环结构会重复执行同样的语句直到根据循环条件判断应该结束循环。

一个完整的循环结构,都应该包含循环的初始状态、循环的结束状态和每次循环之后状态的改变,这三个要素会控制整个循环的执行。通常为了避免出现死循环,在设计循环结构的语句时,应该在执行循环前指定循环的初始状态,每一次循环之后状态的改变都应向着循环的结束状态靠近。

3.1 while循环语句

while循环是Java中最基础的循环语句,是一种条件驱动的循环结构,只要条件表达式返回的结果为true,它就会一直执行循环体中的代码。语法如下所示:

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

while循环语句中,while关键字后括号内容的条件表达式会返回一个布尔值,只要该布尔值为true,就会执行循环体中的语句,当返回的布尔值为false时,就会跳过循环体,执行while循环之后的语句。如果while循环语句的循环体只有一行语句,就可以省略包裹循环体的花括号,但通常不建议这样做。此外,while循环语句还可以省略循环体,省略循环体时,需要直接在包含条件表达式的圆括号之后使用分号结尾。

下面的示例展示了while循环语句的用法。

public class WhileStatement {

	public static void main(String[] args) {
		int i = 5;// 循环的初始状态
		while (i > 0) {// 循环的终止状态
			System.out.println("倒计时:" + i);
			i--;// 循环状态的改变
		}
	}
}

程序执行后的结果如下:

倒计时:5
倒计时:4
倒计时:3
倒计时:2
倒计时:1

上面的程序使用整型变量i值控制while循环,在进入while循环语句之前,赋值i值为10作为while循环的初始状态。然后执行while循环时,在条件表达式中判断i值是否满足循环的条件i>0,满足的话就执行循环体中的输出语句并将变量i自减,不满足的话就终止执行while循环语句。

下面的示例展示了while循环语句不带循环体的用法。

public class WhileStatementWithNobody {

	public static void main(String[] args) {
		int i = 0;// 循环的初始状态
		int j = 100;// 循环的初始状态
		while (++i < --j)// 循环的终止状态和循环状态的改变
			;
		System.out.println("while循环结束后,i = " + i + ",j = " + j + "。");
	}
}

程序执行后的结果如下:

while循环结束后,i = 50,j = 50。

上面的程序使用了一个不包含循环体的while循环语句,在while循环之前赋值i值为0,j值为100作为循环的初始状态,然后在while循环的条件表达式中,完成循环状态的改变和循环终止状态的判断。所以,即使这个while循环语句没有循环体,依然能够正常结束循环。

3.2 do…while循环语句

do…while循环语句在结构上类似while循环语句的倒装,do…while循环语句会先执行循环体中的语句,再根据控制循环的条件表达式的返回值决定是否继续循环的执行。语法如下所示:

do{
   循环体
}while(条件表达式);

do…while循环语句中,循环体语句在do之后的花括号中,花括号之后才是while(条件表达式),所以从结构上看,程序在进行循环条件判断之前,就已经执行了循环体语句。这里需要注意的是,while(条件表达式)之后的英文分号不能省略。

下面的示例展示了do…while循环语句的用法。

public class DoWhileStatement {

	public static void main(String[] args) {
		int i = 5;// 循环的初始状态
		do {
			System.out.println("倒计时:" + i);
			i--;// 循环状态的改变
		} while (i > 0);// 循环的终止状态
	}
}

程序执行后的结果如下:

倒计时:5
倒计时:4
倒计时:3
倒计时:2
倒计时:1

上面的程序将之前使用while循环语句的倒计时改写为do…while循环语句,虽然从执行输出的结果上看,两种循环语句是一样的,但在循环执行的顺序上,一定要注意第一句倒计时:10,while循环是判断了条件表达式之后输出的,而do…while循环是先输出了一次再判断条件表达式。上面的示例还可以使用类似不带循环体的while循环中的简化方式对代码进行优化,优化后的示例如下:

public class DoWhileStatement {

	public static void main(String[] args) {
		int i = 5;// 循环的初始状态
		do {
			System.out.println("倒计时:" + i);
		} while (--i > 0);// 循环状态的改变和循环的终止状态
	}
}

3.3 for循环语句

Java中for循环是最常用的循环语句,它有两种不同的形式。第一种是传统形式,它将循环的三个要素统一放置在for后面的括号中,适用于循环次数固定的场景;第二种是增强形式,它自JDK5开始引入,主要用于循环遍历数组或者集合。

3.3.1 for循环语句的传统形式

for循环的传统形式语法如下所示:

for (初始化表达式; 循环条件; 迭代表达式) {
    // 循环体
}

如果循环体仅有一条语句,从语法上可以像while循环一样省略循环体外侧的花括号,但通常不建议这样。

for循环的执行过程如下:

第一步:在for循环开始前执行一次初始化表达式​,初始化表达式需要声明或初始化循环控制变量(如int i = 0)。必须注意,初始化表达式仅执行一次,它对应循环的初始状态。

第二步:对​循环条件进行求值,循环条件通常是一个布尔表达式(如i < 5)。若循环条件为true则进入第三步执行循环体,否则退出循环,循环结束,它对应循环的终止状态。

第三步:若第二步中循环条件为true,则执行循环体。

第四步:执行​迭代表达式(如i++)​,确保每次循环体执行后更新循环变量,然后从第二步开始重复循环,它对应循环状态的改变。

下面的示例展示了for循环语句传统形式的用法。

public class ForStatement {

	public static void main(String[] args) {
		for (int i = 5; i > 0; i--) {
			System.out.println("倒计时:" + i);
		}
	}
}

程序执行后的结果如下:

倒计时:5
倒计时:4
倒计时:3
倒计时:2
倒计时:1

上面的程序依然实现的是一个倒计时,但对比代码,可以看到对于确定循环次数的场景,for循环语句的代码更加简洁。

3.3.2 for循环的传统形式的变体

  1. 在for循环外访问循环控制变量

在前面的for循环语句传统形式的用法的示例中,在for循环结束之后,循环控制变量i就被销毁了。

for (int i = 5; i > 0; i--) {
			System.out.println("倒计时:" + i);
}
System.out.println("for循环结束后访问i值:" + i);

此时运行程序会引发错误:i cannot be resolved to a variable。这是因为在for循环的初始化表达式中声明的变量,其作用域仅限于for循环语句中。如果需要在for循环外访问循环控制变量,可以将初始化表达式中循环控制变量的声明改写在for循环之前。

下面的示例展示了在for循环外访问循环控制变量的用法。

public class ForStatementVariant1 {

	public static void main(String[] args) {
		int i;// 循环控制变量的声明在for循环之前
		for (i = 5; i > 0; i--) {// for循环的初始化表达式仅对循环控制变量的赋值
			System.out.println("倒计时:" + i);
		}
		System.out.println("for循环结束之后的i值:" + i);
	}
}

程序执行后的结果如下:

倒计时:5
倒计时:4
倒计时:3
倒计时:2
倒计时:1
for循环结束之后的i值:0

上面的程序将循环控制变量i的作用域扩展到了整个main()方法,所以在for循环结束之后仍能访问其值。

  1. 使用逗号增加循环控制条件

for循环的传统形式还有一种特殊的用法,它允许使用多个循环控制变量控制一个for循环。要实现这种控制方式,可以在for循环的初始化表达式和迭代表达式中包含多条语句,多条语句之间使用逗号分开。

下面的示例展示了在for循环中使用逗号增加循环控制条件的用法。

public class ForStatementVariant2 {

	public static void main(String[] args) {
		int i, j;
		for (i = 1, j = 4; i < j; i++, j--) {
			System.out.println("i = " + i);
			System.out.println("j = " + j);
		}
	}
}

程序执行后的结果如下:

i = 1
j = 4
i = 2
j = 3

上面的程序使用i和j两个循环控制变量控制循环,这种变体适用于需要多个循环控制变量的场景。

  1. 可以省略的初始化表达式、循环条件或迭代表达式

for循环中圆括号里的初始化表达式、循环条件和迭代表达式可以选择性的省略,但它们之间的分号不能省略。

下面的示例展示了在for循环中省略初始化表达式和迭代表达式的用法。

public class ForStatementVariant3 {

	public static void main(String[] args) {
		int i = 5;// 循环控制变量的声明和初始化
		for (; i > 0;) {// 省略初始化表达式和迭代表达式
			System.out.println("倒计时:" + i);
			i--;// 迭代表达式在这里
		}
	}
}

程序执行后的结果如下:

倒计时:5
倒计时:4
倒计时:3
倒计时:2
倒计时:1

上面的程序在for循环中圆括号里省略了省略初始化表达式和迭代表达式,但其实初始化表达式和迭代表达式仍在程序中,只是移动了位置。初始化表达式写在for循环语句之前,迭代表达式被放到了循环体语句中。由于初始化表达式在for循环的执行步骤中只在第一步执行一次,所以它被移动到for循环之前并不影响循环的执行步骤。但迭代表达式移动到循环体中以后,就可以改变原来for循环的执行步骤,这也是这种变体的主要适用场景。

for循环圆括号中的循环条件也可以省略,虽然单独的for(;;){}在语法上没有问题,但它就变成了一个无限循环。所以省略了循环条件的for循环,需要在循环体内配合跳转语句结束循环。

下面的示例展示了在for循环中省略初始化表达式和迭代表达式的用法。

public class ForStatementVariant4 {

	public static void main(String[] args) {
		int i = 5;// 循环控制变量的声明和初始化
		for (;;) {// 省略初始化表达式、循环条件和迭代表达式
			System.out.println("倒计时:" + i);
			i--;// 迭代表达式在这里
			if (i < 1) {
				break;// 循环条件在这里
			}
		}
	}
}

程序执行后的结果如下:

倒计时:5
倒计时:4
倒计时:3
倒计时:2
倒计时:1

上面的程序在for循环中圆括号里省略了省略初始化表达式、循环条件和迭代表达式,类似之前的变体,其实只是把循环条件改成一个具体的if语句进行条件判断,如果条件为真,就执行if语句中的break跳出循环。由于if语句在循环体中的位置可以改变,所以这种变体的适用场景也是针对需要改变for循环执行步骤的特殊实现。

3.3.3 for循环语句的增强形式

for循环的增强形式自JDK5开始引入,它无需传统形式中的初始化表达式、循环条件和迭代表达式,就能以严格的顺序形式,从头到尾循环遍历一个数组或者一个实现了Iterable接口的集合对象中的每一个元素。

for循环的增强形式语法如下所示:

for (数据类型 迭代变量 : 数组或集合) { 
	// 循环体中使用迭代变量访问每一个数据或集合的元素
}

for循环的增强形式中的圆括号里,分号将其中的内容分为两个部分,第一部分通过数据类型 迭代变量的形式,声明一个迭代变量,用于在每次循环时按顺序保存数组或集合中的一个未遍历过的元素;第二部分则指定一个需要循环遍历的数组或集合。

注意:由于迭代变量中保存的数据都来自于数据或者集合,所以要求数组或者集合中的元素的数据必须一致,对于数组来说,通常数组元素的数据类型都是相同的,无需多虑,但对于集合来说,由于add()方法接受的参数可能是Object类型,这就需要在集合对象中使用泛型对其中的元素限定统一的数据类型。

下面的示例展示了for循环语句增强形式的用法。

public class ForEachStatement {

	public static void main(String[] args) {
		int[] countdown = { 5, 4, 3, 2, 1 };
		for (int num : countdown) {
			System.out.println("倒计时:" + num);
		}
	}
}

程序执行后的结果如下:

倒计时:5
倒计时:4
倒计时:3
倒计时:2
倒计时:1

上面的程序通过for循环的增强形式从头到尾读取了int数组countdown中的每一个元素。但需要注意的是,在for循环的增强形式中,迭代变量是只读的,即只能通过迭代变量读取数据,而不能通过迭代变量写入数据。

下面的示例展示了for循环语句增强形式中使用迭代变量写入数据的错误用法。

public class ForEachStatementWrongWay {

	public static void main(String[] args) {
		int[] countdown = { 5, 4, 3, 2, 1 };
		
		for (int num : countdown) {
			num = num * 100;
		}
		
		for (int num : countdown) {
			System.out.println("倒计时:" + num);
		}
	}
}

程序执行后的结果如下:

倒计时:5
倒计时:4
倒计时:3
倒计时:2
倒计时:1

上面的程序使用了两个for循环的增强形式访问int数组countdown中的每一个元素,第一个for循环使用迭代变量将其中保存的数据乘以100,第二个for循环使用迭代变量重新读取int数组countdown中的每一个元素,从执行后的结果来看,int数组countdown中的每一个元素并未改变其中保存的原有数据。如果在实际场景中需要对数组或者集合中的元素进行写入操作,那么只能使用for循环的传统形式。

下面的示例展示了for循环语句增强形式中使用迭代变量写入数据的错误用法。

public class ForEachStatementRightWay {

	public static void main(String[] args) {
		int[] countdown = { 5, 4, 3, 2, 1 };
		int size = countdown.length;

		for (int i = 0; i < size; i++) {
			countdown[i] = countdown[i] * 100;
		}

		for (int num : countdown) {
			System.out.println("倒计时:" + num);
		}
	}
}

程序执行后的结果如下:

倒计时:500
倒计时:400
倒计时:300
倒计时:200
倒计时:100

上面的程序先通过for循环的传统形式,利用循环控制变量作为int数组countdown数组元素的索引,改变数组中每一个元素的数据,然后通过for循环的增强形式读取int数组countdown中的每一个元素,从执行后的结果来看,int数组countdown中的每一个元素都改变为新的数据。

注意:使用for循环的传统形式以循环控制变量作为索引的形式访问数组或者集合时,一定要避免索引越界引发ArrayIndexOutOfBoundsException异常。

3.3.4 for循环语句的局部变量类型推断

局部变量类型推断是Java 10引入的重要特性(JEP 286),通过var关键字让编译器自动推断变量的数据类型。局部变量类型推断可以使得for循环显著简化代码。使用局部变量类型推断时,需要在声明变量时使用var关键字作为变量的数据类型,而且必须在声明变量的同时进行赋值。

下面的示例展示了for循环语句中使用局部变量类型推断的用法。

public class TypeInference {

	public static void main(String[] args) {
		for (var i = 5; i > 0; i--) {// 等价于for (int i = 5; i > 0; i--)
			System.out.println("倒计时:" + i);
		}

		int[] countdown = { 5, 4, 3, 2, 1 };
		for (var num : countdown) {// 等价于for (int num : countdown)
			System.out.println("倒计时:" + num);
		}
	}
}

程序执行后的结果如下:

倒计时:5
倒计时:4
倒计时:3
倒计时:2
倒计时:1
倒计时:5
倒计时:4
倒计时:3
倒计时:2
倒计时:1

上面的程序在两种形式的for循环中,在初始化循环控制变量和迭代变量使用var关键字作为它们的数据类型,这样编译器就可以通过保存在其中的具体数据,来推断该变量的数据类型。示例中对基本数据类型进行推断并不能体现局部变量类型推断的优势,在精简代码方面,针对集合泛型的推断更具优势。

4. 跳转语句

在Java中,跳转语句用于控制程序的执行流程,主要包括三种:break语句、continue语句和return语句。

4.1 break语句

Java种break语句主要有三种用法:

  • switch-case选择语句:终止某个case并跳出switch-case选择语句;
  • 循环语句:终止当前循环
  • 代替goto:配合标签实现goto操作

之前介绍switch-case选择语句已经演示了break语句的第一种用法,在此介绍后面两种用法。

4.1.1 循环语句中的break语句

在循环语句种使用break语句可以强制终止当前循环,跳过循环体剩余的代码,执行循环语句之后的代码。

下面的示例展示了循环语句中使用break语句的用法。

public class BreakStatement {

	public static void main(String[] args) {
		int i = 0;
		while (++i > 0) {
			if (i % 6 == 0 && i % 8 == 0) {
				break;
			}
		}
		System.out.println("6和8的最小公倍数是" + i);
	}
}

程序执行后的结果如下:

6和8的最小公倍数是24

上面的程序中声明了一个int类型变量i并赋值为0,由于++i始终大于0,这里的while循环实际上是一个无限循环。每次循环都将i值逐次自增,当i值与6和8求模操作结果都为0时,就执行break语句跳出while循环,执行while循环语句之后的输出语句显示6和8的最小公倍数的计算结果。

需要注意的是,对于嵌套的循环语句,当break出现在内层循环时,它只能跳出内层循环而无法跳出外层循环。

下面的示例展示了嵌套循环语句的用法。

public class NestedLoop {

	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 + "=" + (j * i) + " ");
			}
			System.out.println();
		}
	}
}

程序执行后的结果如下:

1×1=1 
1×2=2 2×2=4 
1×3=3 2×3=6 3×3=9 
1×4=4 2×4=8 3×4=12 4×4=16 
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25 
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 6×6=36 
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 7×7=49 
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40 6×8=48 7×8=56 8×8=64 
1×9=9 2×9=18 3×9=27 4×9=36 5×9=45 6×9=54 7×9=63 8×9=72 9×9=81

上面的程序使用两层for循环进行嵌套,外层for循环控制行,内层for循环控制行内的乘法算式,输出一个九九乘法算式表。如果在内层for循环if语句,当两个乘数相同时执行break语句,这个break不会跳出外层的for循环。

下面的示例展示了嵌套循环语句中内层执行break语句的用法。

public class NestedLoop {

	public static void main(String[] args) {
		for (int i = 1; i <= 9; i++) {
			for (int j = 1; j <= i; j++) {
				if(i == j) break;// 内层break不会跳出外层循环
				System.out.print(j + "×" + i + "=" + (j * i) + " ");
			}
			System.out.println();
		}
	}
}

程序执行后的结果如下:


1×2=2 
1×3=3 2×3=6 
1×4=4 2×4=8 3×4=12 
1×5=5 2×5=10 3×5=15 4×5=20 
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40 6×8=48 7×8=56 
1×9=9 2×9=18 3×9=27 4×9=36 5×9=45 6×9=54 7×9=63 8×9=72 

上面的程序在内层循环发现两层循环控制变量相等时if(i == j),执行了break语句,但是跳出内层循环之后,返回到了外层循环。可以看出,内层执行的break语句对外层循环没有影响。如果在多层嵌套循环中需要跳出循环到指定位置,只能使用break语句的第三种用法——代替goto。

注意,break语句并不是停止循环语句执行的正常手段,循环语句中的循环条件才是停止循环执行的正常手段,break语句用于跳出循环仅在某些特殊场景中应用。

4.1.2 使用break语句代替goto语句

在Java中,goto是一个保留但未实现的关键字,它存在于Java的关键字列表中,这样可以避免goto被用作标识符,但它又未实现,所以不能在编程中使用它进行无条件地转移。

为什么Java不实现goto?

Java设计者做出这个决定主要基于以下原因:

  • 代码可读性问题​:goto可能导致"面条代码",难以跟踪执行流程;
  • ​维护困难​:使用goto的代码难以调试和修改;
  • 结构化编程原则​:编程语言应该更加强调使用循环、条件和方法等结构控制流程;
  • 替代方案充足​:Java的break、continue和return等语句已经提供了足够的流程控制

为了代替goto,Java提供了break语句的一种扩展形式,通过使用这种带有标签的break语句,可以中断一个或多个任意代码块,这些代码块不局限于switch-case或者循环语句。

带有标签的break语句的扩展形式一般如下所示:

break 标签名;

标签名主要用于标识一个代码块。这个被标识的代码块可以是独立的,也可作为另一条语句的目标。当break 标签名;被执行时,程序的执行控制会跳出由标签名指定的代码块。

为代码块指定标签名时,直接在代码块前放置一个标签,标签之后用冒号与代码块相邻,标签可以是任意合法的Java标识符。

注意,具有标签名的代码块中,必须包含break 标签名;语句。

下面的示例展示了break语句这种扩展形式的用法。

public class BreakLabelStatement {

	public static void main(String[] args) {
		boolean flag = true;

		first: {
			second: {
				third: {
					System.out.println("这是在执行break second语句之前的输出。");
					if (flag) break second;
					System.out.println("这是在执行break second语句之后的不会执行的输出。");
				} // third标识的代码块结束
				System.out.println("这是second代码块最后不会执行的输出。");
			} // second标识的代码块结束
			System.out.println("这是first代码块最后会执行的输出。");
		} // first标识的代码块结束
	}
}

程序执行后的结果如下:

这是在执行break second语句之前的输出。
这是first代码块最后会执行的输出。

上面的程序中标识了3个嵌套的代码块,在第三层的third代码块中使用break second;跳出到第二层的second代码块之外。程序执行后,second代码块和third代码块结尾处的输出都因为break语句跳出而没有执行。在编辑器中可以删除if (flag),不会执行输出的语句会提示Unreachable code

4.2 continue语句

continue语句适用于任何循环控制结构中,其作用是忽略当前循环体中的剩余代码,让程序立刻跳转,继续执行下一次循环。

  • 在while和do…while循环中,continue会跳转到执行控制循环条件的表达式。
  • 在for循环的传统形式中,continue会跳转到括号中的迭代表达式,然后再执行控制循环条件的表达式。

下面的示例展示了continue语句的用法。

public class ContinueStatement {

	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			System.out.print(i + " ");
			if (i % 2 == 0) continue;
			System.out.println("");
		}
	}
}

程序执行后的结果如下:

0 1 
2 3 
4 5 
6 7 
8 9 

上面的程序使用求模运算符判断每次循环中i值是否为偶数,如果是偶数就继续循环(不执行continue之后输出的换行。

continue语句也可以使用标签名,控制继续执行那个包含它的循环。

下面的示例展示了continue语句指定标识名的用法。

public class ContinueLabelStatement {

	public static void main(String[] args) {
		outter: for (int i = 1; i <= 9; i++) {
			for (int j = 1; j <= 9; j++) {
				if (j > i) {
					System.out.println();
					continue outter;
				}
				System.out.print(j + "×" + i + "=" + (j * i) + " ");
			}
			System.out.println();
		}
	}
}

程序执行后的结果如下:

1×1=1 
1×2=2 2×2=4 
1×3=3 2×3=6 3×3=9 
1×4=4 2×4=8 3×4=12 4×4=16 
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25 
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 6×6=36 
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 7×7=49 
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40 6×8=48 7×8=56 8×8=64 
1×9=9 2×9=18 3×9=27 4×9=36 5×9=45 6×9=54 7×9=63 8×9=72 9×9=81  

上面的程序和之前for循环的嵌套的示例输出的九九乘法表结果一致。区别主要在于continue语句,终止当前由循环变量j进行计数的内层循环,并继续执行由循环变量i进行计数的外层循环的下一次循环。

4.3 return语句

return语句是Java中最重要的跳转语句,主要有三大关键作用:

  1. 终止执行​方法​:立即退出当前方法的执行,忽略剩余代码
  2. ​返回计算​结果​:将计算结果传递给调用者
  3. 管理​​执行路径​:改变程序的正常执行流程

添加新评论