与任何程序设计语言一样,Java使用条件语句和循环结构确定控制流程。当需要对某个表达式的多个值进行检测时,可以使用switch语句。
在深入学习控制结构之前,需要了解块(block)的概念。
块(即复合语句)是指由若干条Java语句组成的语句,并用一对大括号括起来。块确定了变量的作用域。一个块可以嵌套在另一个块中。下面就是嵌套在main方法块中的一个块。
public static void main(String[] args) { int n; ... { int k; } }//k is only defined up to here
但是,不能在嵌套的两个块中声明同名的变量。例如,下面的代码就错误,而无法通过编译:
public static void main(String[] args) { int n; ... { int k; int n; //ERROR--can't redefine n in inner block ... } }
在Java中,条件语句的形式为
if(condition) statement
这里的条件必须用小括号括起来。
与绝大多数程序设计语言一样,Java常常希望在某个条件为真时执行多条语句。在这种情况下,就可以使用块语句(block statement),形式为
{ statement1 statement2 ... }
例如:
if(yourSales >= target) { performance="Satisfactory"; bonus=100; }
当yourSales大于或等于target时,将执行大括号中的所有语句(请参看图(if语句流程图))。
使用块(有时称为复合语句)可以在Java程序结构中原本只能放置一条(简单)语句的地方放置多条语句。
在Java中,更一般的条件语句如下所示(请参看图(if或else语句流程图)):
if(condition) statement1 else statement2
例如:
if(yourSales >= target) { performance = "Satisfactory"; bonus = 100+0.01*(yourSales - target); } else { performance = "Unsatisfactory"; bonus = 0; }
public class HuangZiHanTest { public static void main(String[] args) { int tall =177; int weight =160; String huangzihan_performance; //double bonus; if(tall >= weight) { huangzihan_performance = "黄子涵是帅哥!"; //bonus = 100+0.01*(tall - weight); System.out.println(huangzihan_performance); } else { huangzihan_performance = "黄子涵不是帅哥!"; //bonus = 0; } } }
黄子涵是帅哥!
其中else部分总是可选的。else子句与最邻近的if构成一组。因此,在语句if
中else与第2个if配对。当然,使用大括号会让这段代码更加清晰:
0 黄子涵是帅哥!
反复使用if...else if...很常见(请参看图(if或else if(多分支)流程图))。例如:
if(yourSales >= 2* target) { performance = "Excellent"; bonus = 1000; } else if (yourSales >= 1.5* target) { performance = "Fine"; bonus = 500; } else if (yourSales >= target) { performance = "Satisfactory"; bonus = 100; } else { System.out.println("You're fired"); }
public class HuangZiHanTest { public static void main(String[] args) { int huangzihan_tall=177; int huangzihan_weight=160; String huangzihan_performance; int bonus; if(huangzihan_tall >= 2* huangzihan_weight) { huangzihan_performance = "黄子涵不是帅哥!"; bonus = 1000; System.out.println(bonus); System.out.println(huangzihan_performance); } else if (huangzihan_tall >= 1.5* huangzihan_weight) { huangzihan_performance = "黄子涵可能是帅哥!"; bonus = 500; System.out.println(bonus); System.out.println(huangzihan_performance); } else if (huangzihan_tall >= huangzihan_weight) { huangzihan_performance = "黄子涵肯定是帅哥!"; bonus = 100; System.out.println(bonus); System.out.println(huangzihan_performance); } else { System.out.println("你眼光很好!"); } } }
100 黄子涵肯定是帅哥!
当条件为true时,while循环执行一条语句(也可以是一个块语句)。一般形式如下
while(condition) statement
如果开始时循环条件的值就为false,那么while循环一次也不执行(请参看图(while语句的流程图))。
while循环语句在最前面检测循环条件。因此,循环体中的代码有可能一次都不执行。如果希望循环体至少执行一次,需要使用do/while循环将检测放在最后。它的语法如下:
do statement while (condition);
这种循环语句先执行语句(通常是一个语句块),然后再检测循环条件。如果为true,就重复执行语句,然后再次检测循环条件,以此类推。
你需要多少钱退休? 987654321 你每年捐多少钱? 12345.6789 利率(%): 6.78 你可以在131年后退休。
/* * 此程序演示<code>do/while</code>循环。 * 版本:1.01 * 作者:黄子涵 * 时间:2021年7月7日 * */ import java.util.Scanner; public class HuangZiHanTest { public static void main(String[] args) { Scanner in = new Scanner(System.in); System.out.println("你每年捐多少钱?"); double huangzihan_payment = in.nextDouble(); System.out.println("利率(%):"); double huangzihan_interestRate = in.nextDouble(); double huangzihan_balance = 0; int huangzihan_years = 0; String huangzihan_input; do { huangzihan_balance += huangzihan_payment; double huangzihanInterest = huangzihan_balance * huangzihan_interestRate / 100; huangzihan_balance += huangzihanInterest; huangzihan_years++; System.out.printf("第%d年后,您的余额为%,.2f%n",huangzihan_years,huangzihan_balance); System.out.println("准备退休吗?(是/否)"); huangzihan_input = in.next(); } while(huangzihan_input.equals("否")); } }
你每年捐多少钱? 98765.4321 利率(%): 6.78 第1年后,您的余额为105,461.73 准备退休吗?(是/否) 否 第2年后,您的余额为218,073.76 准备退休吗?(是/否) 否 第3年后,您的余额为338,320.89 准备退休吗?(是/否) 否 第4年后,您的余额为466,720.78 准备退休吗?(是/否) 否 第5年后,您的余额为603,826.17 准备退休吗?(是/否) 是
for循环语句是支持迭代的一种通用结构,由一个计数器或类似的变量控制迭代次数,每次迭代后这个变量将会更新。如图(for语句流程图)所示,下面的循环将数字1 ~ 10输出到屏幕上。
for语句的第1部分通常是对计数器初始化;第2部分给出每次新一轮循环执行前要检测的循环条件;第3部分指定如何更新计数器。
for语句的3个部分应该对同一个计数器变量进行初始化、检测和更新。
1 2 3 4 5 6 7 8 9 10
在循环中,检测两个浮点数是否相等需要格外小心。下面的for循环
for(double x = 0; × != 10; x += 0.1) ...
可能永远不会结束。由于舍入的误差,可能永远达不到精确的最终值。例如,在上面的循环中,因为0.1无法精确地用二进制表示,所以,x将从9.999 999 999 999 98跳到10.099 999 999 999 98。
public class HuangZiHanTest { public static void main(String[] args) { for(double huangzihan_x = 0; huangzihan_x != 10; huangzihan_x += 0.1) { System.out.println("黄子涵是帅哥!"); } } }
黄子涵是帅哥! 黄子涵是帅哥! 黄子涵是帅哥! ... ... ...
当在for语句的第1部分中声明了一个变量之后,这个变量的作用域就扩展到这个for循环体的末尾。
1 2 3 4 5 6 7 8 9 10 黄子涵是帅哥!
特别指出,如果在for语句内部定义一个变量,这个变量就不能在循环体之外使用。因此,如果希望在for循环体之外使用循环计数器的最终值,就要确保这个变量在循环之外声明!
黄子涵是帅哥! 黄子涵是帅哥! 黄子涵是帅哥! 黄子涵是帅哥! 黄子涵是帅哥! 黄子涵是帅哥! 黄子涵是帅哥! 黄子涵是帅哥! 黄子涵是帅哥! 黄子涵是帅哥! 11
另一方面,可以在不同的for循环中定义同名的变量:
黄子涵是帅哥吗? 黄子涵是帅哥吗? 黄子涵是帅哥吗? 黄子涵是帅哥吗? 黄子涵是帅哥吗? 黄子涵是帅哥吗? 黄子涵是帅哥吗? 黄子涵是帅哥吗? 黄子涵是帅哥吗? 黄子涵是帅哥吗? 肯定啊! 肯定啊! 肯定啊! 肯定啊! 肯定啊! 肯定啊! 肯定啊! 肯定啊! 肯定啊! 肯定啊!
for循环语句只不过是while循环的一种简化形式。例如,
for(int i = 10; i > 0; i--) System.out.println("Counting down . . ."+i);
可以重写为:
int i = 10; while(i > 0) { System.out.println("Counting down . . ." + i); i --; }
public class HuangZiHanTest { public static void main(String[] args) { for(int huangzihan_i = 10; huangzihan_i > 0; huangzihan_i--) { System.out.println("黄子涵是帅哥 . . ."+huangzihan_i); } System.out.println(); int huangzihan_i = 10; while(huangzihan_i > 0) { System.out.println("黄子涵是帅哥 . . ." + huangzihan_i); huangzihan_i --; } } }
黄子涵是帅哥 . . .10 黄子涵是帅哥 . . .9 黄子涵是帅哥 . . .8 黄子涵是帅哥 . . .7 黄子涵是帅哥 . . .6 黄子涵是帅哥 . . .5 黄子涵是帅哥 . . .4 黄子涵是帅哥 . . .3 黄子涵是帅哥 . . .2 黄子涵是帅哥 . . .1 黄子涵是帅哥 . . .10 黄子涵是帅哥 . . .9 黄子涵是帅哥 . . .8 黄子涵是帅哥 . . .7 黄子涵是帅哥 . . .6 黄子涵是帅哥 . . .5 黄子涵是帅哥 . . .4 黄子涵是帅哥 . . .3 黄子涵是帅哥 . . .2 黄子涵是帅哥 . . .1
这个程序用来计算抽奖中奖的概率。例如,如果必须从1 ~ 50的数字中取6个数字来抽奖,那么会有(50×49×48×47×46×45)/(1×2×3×4×5×6)种可能的结果,所以中奖的概率是1/15 890 700。祝你好运!
一般情况下,如果从n个数字中抽取k个数字,就会有
n*(n-1)*(n-2)*...*(n-k+1) ------------------------------ 1*2*3*4*...*k
种可能。下面的for循环语句可以计算这个值:
你需要画多少个数字? 5 你能画的最高数字是多少? 10 你的赔率是1/252。祝你好运!
在处理多个选项时,使用if/else结构显得有些笨拙。
例如,如果建立一个如图(for语句流程图)所示的包含4个选项的菜单系统,可以使用下列代码:
Scanner in = new Scanner(System.in); System.out.print("Select an option(1,2,3,4)"); int choice = in.nextInt(); Switch(choice) { case 1: . . . break; case 2: . . . break; case 3: . . . break; case 4: . . . break; default: //bad input . . . break; }
import java.util.Scanner; public class HuangZiHanTest { public static void main(String[] args) { while(true) { Scanner huangzihan_in = new Scanner(System.in); System.out.print("选择一个数字(1,2,3)"); int huangzihan_choice=huangzihan_in.nextInt(); switch(huangzihan_choice) { case 1: System.out.println("黄子涵真帅!"); break; case 2: System.out.println("黄子涵真高!"); break; case 3: System.out.println("黄子涵真有钱!"); break; } } } }
选择一个数字(1,2,3) 1 黄子涵真帅! 选择一个数字(1,2,3) 2 黄子涵真高! 选择一个数字(1,2,3) 3 黄子涵真有钱! 选择一个数字(1,2,3)
switch语句将从与选项值相匹配的case标签开始执行,直到遇到break语句,或者执行到switch语句的结束处为止。如果没有相匹配的case标签,而有default子句,就执行这个子句。
有可能触发多个case分支。如果在case分支语句的末尾没有break语句,那么就会接着执行下一个case分支语句。这种情况相当危险,常常会引发错误。为此,我们在程序中从不使用switch语句。
如果你比我们更喜欢switch语句,编译代码时可以考虑加上
-XLint:fallthrough
选项,如下所示:
javac -XLint:fallthrough Test.java
这样一来,如果某个分支最后缺少一个break语句,编译器就会给出一个警告消息。
如果你确实正是想使用这种“直通式”(fallthrough)行为,可以为其外围方法加一个注解
@SuppressWarnings("fallthrough")
。这样就不会对这个方法生成警告了。(注解是为编译器或处理Java源文件或类文件的工具提供信息的一种机制。)
case标签可以是:
例如:
String input = . . .; switch(input.toLowerCase()) { case "yes": //OK since Java 7 . . . break; . . . }
使用默认区域设置的规则将此字符串中的所有字符转换为小写。这相当于调用toLowerCase(Locale.getDefault())。
此方法对区域设置敏感,如果用于要在本地独立解释的字符串,则可能会产生意外结果。例如,编程语言标识符、协议密钥和HTMLtags。例如,土耳其语locale中的"TITLE".toLowerCase()
返回“t\u005Cu0131tle”
,其中"\u005Cu0131"是LATIN SMALL LETTER DOTLESS I 字符。若要获得不区分区域设置的字符串的正确结果,请使用toLowerCase(Locale.ROOT)。
字符串,转换为小写。
public class HuangZiHanTest { public static void main(String[] args) { while(true) { String huangzihan_input="是"; switch(huangzihan_input.toLowerCase()) { case "是": System.out.println("黄子涵是帅哥!"); break; case "否": System.out.println("黄子涵不是帅哥!"); break; } } } }
黄子涵是帅哥! 黄子涵是帅哥! 黄子涵是帅哥! ...... ...... ...... 黄子涵是帅哥! 黄子涵是帅哥! 黄子涵是帅哥! ...... ...... ......
当在switch语句中使用枚举常量时,不必在每个标签中指明枚举名,可以由switch的表达式值推导得出。例如:
Size sz = . . .; Switch(sz) { case SMALL: //no need to use Size.SMALL break; . . . }
尽管Java的设计者将goto作为保留字,但实际上并没有打算在语言中使用它。通常,使用goto语句被认为是一种拙劣的程序设计风格。当然,也有一些程序员认为反对goto的呼声似乎有些过分(例如,Donald Knuth就曾写过一篇名为《Structured Programming with goto statements》的著名文章)。这篇文章说,无限制地使用goto语句确实很容易导致错误,但在有些情况下,偶尔使用goto跳出循环还是有益处的。Java设计者同意这种看法,甚至在Java语言中增加了一条新的语句:带标签的break,以此来支持这种程序设计风格。
下面首先看一下不带标签的break语句。与用于退出switch语句的break语句一样,它也可以用于退出循环语句。例如,
50 12.0 62 1 50 28.0 140 2 50 47.0 237 3 50 71.0 358 4 50 102.0 510 5 50 140.0 700 6 50 187.0 937 7 50 246.0 1233 8
在循环开始时,如果years>100,或者在循环体中balance≥goal,则退出循环语句。当然,也可以在不使用break的情况下计算years的值,如下所示:
50 12.0 62 1 50 28.0 140 2 50 47.0 237 3 50 71.0 358 4 50 102.0 510 5 50 140.0 700 6 50 187.0 937 7 50 246.0 1233 8 50 320.0 1603 8
但是需要注意,在这个版本中,检测了两次balance<goal。为了避免重复检测,有些程序员更加偏爱使用break语句。
Java还提供了一种带标签的break语句,用于跳出多重嵌套的循环语句。
有时候,在嵌套很深的循环语句中会发生一些不可预料的事情。此时可能更加希望完全跳出所有嵌套循环之外。如果只是为各层循环检测添加一些额外的条件,这会很不方便。
这里有一个示例说明了break语句的工作状态。请注意,标签必须放在希望跳出的最外层循环之前,并且必须紧跟一个冒号。
如果输入有误,执行带标签的break会跳转到带标签的语句块末尾。与任何使用break语句的代码一样,然后需要检测循环是正常结束,还是由break跳出。
事实上,可以将标签应用到任何语句,甚至可以将其应用到if语句或者块语句,如下所示:
label: { . . . if(condition) break label; //exits block . . . } // jumps here when the break statement executes
public class HuangZiHanTest { public static void main(String[] args) { String huangzihan="黄子涵是帅哥!"; label: { if(huangzihan=="黄子涵是帅哥!") { System.out.println("帅哥,黄子涵!"); break label; } } System.out.println("黄子涵是帅哥!"); } }
帅哥,黄子涵! 黄子涵是帅哥!
因此,如果确实希望使用goto语句,而且一个代码块恰好在你想要跳到的位置之前结束,就可以使用break语句!当然,并不提倡使用这种方式。另外需要注意,只能跳出语句块,而不能跳入语句块。
最后,还有一个continue语句。与break语句一样,它将中断正常的控制流程。continue语句将控制转移到最内层循环的首部。例如:
如果n<0,则continue语句越过了当前循环体的剩余部分,立刻跳到循环首部。
输入一个数字: 2 huangzihan_sum=2 输入一个数字: 3 huangzihan_sum=5 输入一个数字: -1 输入一个数字: 0 huangzihan_sum=5 输入一个数字: 4 huangzihan_sum=9 输入一个数字: -5 输入一个数字: 6 huangzihan_sum=15
如果将continue语句用于for循环中,就可以跳到for循环的“更新”部分。例如,下面这个循环:
如果n<0,则continue语句将跳到
count++
语句。
还有一种带标签的continue语句,将跳到与标签匹配的循环的首部。
输入一个数字,-1退出: 2 huangzihan_count=1 huangzihan_sum=2 输入一个数字,-1退出: -1 输入一个数字,-1退出: 3 huangzihan_count=3 huangzihan_sum=5 输入一个数字,-1退出: -1 输入一个数字,-1退出: 4 huangzihan_count=5 huangzihan_sum=9
许多程序员发现很容易混淆break和continue语句。这些语句完全是可选的,即不使用它们也可以表达同样的逻辑含义。