【Java流程笔记】2-2 程序控制结构
§2-2 程序控制结构
Java 语言的结构主要有三种:顺序结构、选择结构和循环结构,这三种结构基本可以满足所有程序的运行要求。下面将对该三种结构展开描述。
2-2.1 顺序结构
Java 的基本结构就是顺序结构,是最简单的算法结构,除非特别指明,否则就按照顺序一句一句地执行。
语句和语句之间,程序框与程序框之间,都是自上而下的顺序执行的,它是由若干个依次执行的步骤组成的,是任何一个算法都离不开的基本算法结构。
顺序结构可以用下图简单表示:
2-2.2 选择结构
在很多种情景下,我们需要对可能出现的各种结果作出不同回应,这时就需要针对不同情况做出选择。程序运行亦是如此,Java 的选择结构有if
和switch
结构。
2-2.2.1 if
单选择结构
语法:
if (布尔表达式) {
//当表达式结果为true时,执行语句
}
这种结构适用于判断一个东西是否可行,可行则执行if
内语句,否则跳出判断。
举个例子:
public class IfDemo01 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//if 单选择
System.out.println("Please input: ");
String s = scanner.nextLine();
//equals.() 用于判断字符串内容是否相等
if (s.equals("Hello")) {
System.out.println(s);
}
System.out.println("END.");
scanner.close();
}
}
2-2.2.2 if
双选择结构
语法:
if (布尔表达式) {
//条件满足时执行此语句
} else {
//否则执行此语句
}
举个例子:
public class IfDemo02 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//及格与不及格的判断:小于等于 60 分为不及格,60 分至 100 分为及格
System.out.println("Please input the score: ");
int score = scanner.nextInt();
if (score <= 60) {
System.out.println("Exam passed. ");
} else {
System.out.println("Exam failed. ");
}
scanner.close();
}
}
2-2.2.3 if
多选择结构
语法:可以添加多个else if
,但执行时只会选择条件满足的一个执行,否则执行else
后跳出结构。
if (布尔表达式) {
//若该条件满足,执行该语句后跳出选择结构
} else if (布尔表达式) {
//若该条件满足,执行该语句后跳出选择结构
} else if (布尔表达式) {
//若该条件满足,执行该语句后跳出选择结构
} else {}
可以看到 2-2.2.2 例子所给的及格判断还有漏洞:若所输入的成绩不在 0 ~ 100 范围内该如何?同时欲给不同成绩分级该如何解决?
以下是解决方案:
public class IfDemo03 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//判断所输入是否合法,并且分级
double score = scanner.nextDouble();
if (score >=0 && score < 60) {
System.out.println("Exam failed. ");
} else if (score >=60 && score < 70) {
System.out.println("Level D");
} else if (score >=70 && score < 80) {
System.out.println("Level C");
} else if (score >=80 && score < 90) {
System.out.println("Level B");
} else if (score >=90 && score < 100) {
System.out.println("Level A");
}else if (score == 100) {
System.out.println("Full score! ");
}else {
System.out.println("Illegal score. ");
}
scanner.close();
}
}
注意:else if
必须在else
前,且多选择结构只会按顺序结构执行满足条件的语句执行,执行完后忽略其后的所有剩余else if
和else
结构。
2-2.2.4 if
嵌套结构
语法:
if () {
if () {}
}
在if
选择结构中再嵌套一个if
选择结构也是合法的,利用这一点,我们来思考一个问题:
如何寻找一个1 - 100之间的数?
结合二分法,利用嵌套if
结构,可以较快地解决这一问题。
2-2.2.5 switch
多选择结构
多选择结构还有一个实现方法就是switch case
语句。
switch case
语句判断一个变量与一系列值中的某个值是否相等,每一个值称为一个分支。
语法:
switch (expression) {
case value:
//语句
break; //可选
case value:
//语句
break; //可选
//可接多个 case
default: //可选
//语句
}
注意:
switch
语句中的变量类型可以是byte
、short
、int
或char
;- 从 Java SE 7 开始,
switch
支持字符串String
类型; case
标签必须为字符串常量或字面量。
下面来看一个例子:
public class SwitchDemo01 {
public static void main(String[] args) {
char grade = 'C';
switch (grade) {
case 'A':
System.out.println("Excellent");
case 'B':
System.out.println("Great");
case 'C':
System.out.println("Good");
case 'D':
System.out.println("Almost there");
case 'E':
System.out.println("Oops!!");
default: //对于其他情况
System.out.println("Unknown grades");
}
}
}
编译后运行,结果如图所示:
可以看到,当匹配到值C
后,其后的两个case
分支也都运行了,这种现象称为case
穿透,为防止这种现象发生,建议在每个case
分支下加入break
语句。修改后再运行,得到结果如图所示:
小提示:可以在文件资源管理器中将编译好的.class
字节码文件拖入 IDEA 工程中,在 IDEA 中反编译.class
文件。可以看到,若源文件使用了字符串和switch
结构,可以看到字符串被转换成哈希值的过程(即所有的字符本质上都是数字)。
2-2.3 循环结构
我们有时会需要程序一直运行,基于这种需求,Java 内置了几种循环结构:while
do...while
和for
(JDK 5 加入)循环。
2-2.3.1 while
循环
while
循环是最基本的循环,语法:
while (布尔表达式) {
//循环内容
}
注意:
- 只要布尔表达式的值为
true
,循环就会一直进行; - 大多数情况下,我们需要让表达式失效以终止循环;
- 少部分循环需一直进行,如服务器请求监听等,这时候表达式可以为
true
; - 循环条件一直为
true
会导致程序陷入死循环(无限循环),正常情况下应避免死循环,否则会影响程序性能并导致程序崩溃死机。
2-2.3.2 do...while
循环
有时候我们也会希望程序在循环条件不满足的情况下也要至少执行一次,这时就需要使用do..while
语句作循环了。
语法:
do {
//循环内容
}while (布尔表达式);
区别:do...while
语句是先执行,后判断;而while
先判断,后执行。这可保证循环体至少被执行一次;
举个例子:
public class DoWhileDemo01 {
public static void main(String[] args) {
int a = 0;
while (a<0){
System.out.println(a);
a++;
}
System.out.println("==================");
do {
System.out.println(a);
}while (a<0);
}
}
编译后运行,得到结果如图:
可以看到,由于while
先判断,条件不满足而跳过了该循环,而do...while
即使在条件不满足的情况下仍至少执行了一次。这体现了 Java 的顺序结构。
2-2.3.3 for
循环
虽然所有循环结构都可以用while
或 do...while
表示,但 Java 提供了另一种语句 for
,使得一些循环结构变得更加简单。
for
语句是支持迭代的一种通用结构,是最有效、最灵活的循环结构。for
循环执行次数在执行前就已确定,语法:
for (初始化变量;布尔表达式;更新迭代) {
//代码语句
}
我们可以通过以下练习熟悉for
语句:
练习 I :计算 0 到 100 之间奇数和与偶数和
利用for
语句:
public class ForDemo01 {
public static void main(String[] args) {
//输出 0 到 100 的奇数和与偶数和
int oddSum = 0;
int evenSum = 0;
for (int i = 0; i <= 100; i++) {
//判断当前数是奇是偶
if ( i % 2 !=0) {//取余运算
oddSum+=i;
}else {
evenSum+=i;
}
}
System.out.println("奇数和为"+oddSum);
System.out.println("偶数和为"+evenSum);
}
}
编译后运行,得下图结果:
练习 II:使用循环结构输出1 - 1000之间可被 5 整除的数,并且每行输出 3 个
使用for
循环:
ublic class ForDemo02 {
public static void main(String[] args) {
for (int i = 0; i <= 1000; i++) {
if ( i % 5 == 0) {
System.out.print(i+"\t");
}
if (i % 15 == 0) {
System.out.println();
}
}
}
}
使用while
循环:
public class WhileDemo02 {
public static void main(String[] args) {
int i = 0; //初始化
while ( i <= 1000){
if ( i % 5 ==0) {
System.out.print(i+"\t");
}
if ( i % 15 == 0) {
System.out.println();
}
//循环体
i++; //更新迭代
}
}
}
练习 III:打印九九乘法表
public class ForDemo03 {
public static void main(String[] args) {
for (int i1 = 1; i1 <= 9; i1++) {
for (int i2 = 1; i2 <= 9; i2++) {
System.out.print(i1+" x "+i2+" = "+(i1*i2)+"\t");
}
System.out.println();
}
}
}
编译后运行,得到结果如图所示:
去掉重复项, i2 <= i1
,有:
注意:
-
println
会在每次输出后换行,而print
不会换行。 -
for
会先执行初始化步骤,可以声明一种类型,也可以初始化一个或多个循环控制变量,也可以是空语句; -
然后检测布尔表达式的值,若为
true
,执行循环体,否则循环终止,执行循环体后面的语句; -
执行一次循环后,更新循环控制变量(即控制迭代变量);然后再次检测布尔表达式的值,循环执行上述过程。
for (;;) { //这是一个死循环 }
-
在 IDEA 中,可用快捷方式快速创建
for
循环:例如:
100.for
会创建:for (int i = 0; i < 100; i++) {}
练习 IV:打印一个 5 行的等边三角形
public class TestDemo {
public static void main(String[] args) {
//打印一个三角形:5 行
for (int i = 1; i <= 5; i++) {
for (int j = 5; j >= i; j--) {
System.out.print(" "); //打印空白三角形
}
for (int j = 1; j <= i; j++) {
System.out.print("*"); // 打印左半部分三角形
}
for (int j = 1; j < i; j++) {
System.out.print("*"); //打印右半部分三角形
}
System.out.println();
}
}
}
在这里,我们需要分析这个问题的解决思路:
-
将打印的三角形补全为一个矩形;
-
以三角形底边中点为垂足,作出三角形的高,平分该矩形;
-
自此,打印内容至少被分割为三部分:左半部分空白三角形、左半部分非空白三角形和右半部分剩余三角形;
-
只需依次打印这些三角形即可。
整理,如图所示:
提示:当遇到较为难理解的地方时,可以在有疑惑的代码所在行行首单击设置断点(breakpoint),然后单击 Debug(或按下Shift + F9
)开启调试,逐步查看程序中变量及其他的变化,以更好地查找问题。
2-2.3.4 for
的增强用法
自从 Java SE 5 开始,引入了一种主要用于数组的增强型for
循环。语法:
for (声明语句:表达式) {
//代码语句
}
举个例子:
public class ForDemo04 {
public static void main(String[] args) {
//定义一个数组
int[] numbers = {10,20,30,40,50};
//增强型 for 循环:创建新变量 x 用于遍历数组中每一个元素
for (int x:numbers) {
System.out.println(x);
}
System.out.println("=============================");
//常规 for 循环:数组内元素下标从 0 开始
for (int i = 0; i < 5 ; i++) {
System.out.println(numbers[i]); //将数组内第 i 个元素赋值于 i
}
}
}
两种情况运行结果一致:
2-2.4 break
、continue
和goto
关键字
break
在任何循环语句主题部分,均可用于控制循环的流程,用于强制退出循环,不执行循环中剩余的语句。(也可在switch
中使用)
continue
语句用在循环主体中,用于终止某次循环,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。
关于goto
关键字:
goto
关键字很早就在设计语言中出现,尽管goto
仍是 Java 的一个保留字,但尚未得到正式使用,也不推荐使用。Java 没有goto
,然而,在break
和continue
这两个关键字身上,仍有一些goto
的影子:带标签的break
和continue
。
标签:后面跟着一个冒号的标识符,如label:
对 Java 来说唯一用到标签的地方是在循环语句之前,而在循环语句前设置标签的唯一理由是:我们希望在其中嵌套另一个循环,由于break
和continue
只中断当前循环,但若与标签使用,它们就会中断到标签存在的地方。
举个例子:
public class LabelDemo01 {
public static void main(String[] args) {
//求 101 - 150 的质数:
int count = 0;
outer:for (int i = 101; i < 150;i++) {
for (int j = 2; j < i/2 ; j++) {
if (i % j == 0) {
continue outer;
}
}
System.out.print(i+" ");
}
}
}
但这种写法不建议使用!
因为goto
关键字可以让程序毫无条件地跳转到任意想要执行的区块上,这种写法在编写大型程序中往往显得不易于维护,也不易于追踪程序运行的逻辑顺序,容易使程序结构混乱。但是,合理使用goto
关键字,可以提升程序可读性,这种情况是建立在禁止滥用goto
关键字的前提上的。