`
fulerbakesi
  • 浏览: 562174 次
文章分类
社区版块
存档分类
最新评论

java基础教程

 
阅读更多
编程语言使用控制(control)语句来产生执行流,从而完成程序状态的改变,如程序顺序执行和分支执行。Java的程序控制语句分为以下几类:选择,重复和跳转。根据表达式结果或变量状态选择(Selection)语句来使你的程序选择不同的执行路径。重复(Iteration)语句使程序能够重复执行一个或一个以上语句(也就是说,重复语句形成循环)。跳转(Jump)语句允许你的程序以非线性的方式执行。下面将分析Java 的所有控制语句。
如果你熟悉C/C++,那么掌握Java的控制语句将很容易。事实上,Java的控制语句与C/C++中的语句几乎完全相同。当然它们还是有一些差别的——尤其是break语句与continue语句。
Java支持两种选择语句:if语句和switch语句。这些语句允许你只有在程序运行时才能知道其状态的情况下,控制程序的执行过程。如果你没有C/C++的编程背景,你将会为这两个语句的强大功能和灵活性而感到吃惊。
5.1.1 if语句
if语句曾在第2章中介绍过,我们将在这里对它进行详细讨论。if语句是Java中的条件分支语句。它能将程序的执行路径分为两条。if语句的完整格式如下:
if (condition) statement1;
else statement2;
其中,if和else的对象都是单个语句(statement),也可以是程序块。条件condition可以是任何返回布尔值的表达式。else子句是可选的。
if语句的执行过程如下:如果条件为真,就执行if的对象(statement1);否则,执行else的对象(statement2)。任何时候两条语句都不可能同时执行。考虑下面的例子:
int a,b;
// ...
if(a < b) a = 0;
else b = 0;
本例中,如果a小于b,那么a被赋值为0;否则,b被赋值为0。任何情况下都不可能使a和b都被赋值为0。
通常,用于控制if语句的表达式都包含关系运算符。当然,这在技术上并不是必要的。仅用一个布尔值来控制if语句也是可以的,如下面的程序段:
boolean dataAvailable;
// ...
if (dataAvailable)
ProcessData();
else
waitForMoreData();
记住,直接跟在if 或else语句后的语句只能有一句。如果你想包含更多的语句,你需要建一个程序块,如下面的例子:
int bytesAvailable;
// ...
if (bytesAvailable > 0) {
ProcessData();
bytesAvailable -= n;
} else
waitForMoreData();
这里,如果变量bytesAvailable 大于0,则if块内的所有语句都会执行。
一些程序员觉得在使用if语句时在其后跟一个大括号是很方便的,甚至在只有一条语句的时侯也使用大括号。这使得在日后添加别的语句变得容易,并且你也不必担心忘记括号。事实上,当需要定义块时而未对其进行定义是一个导致错误的普遍原因。例如,考虑下面的程序段:
int bytesAvailable;
// ...
if (bytesAvailable > 0) {
ProcessData();
bytesAvailable -= n;
} else
waitForMoreData();
bytesAvailable = n;
由于编排的原因,看起来似乎 bytesAvailable = n语句应该在else子句中执行。然而,当你调用时,空白对Java无关紧要,编译器无法知道你的意图。这段程序会通过编译,但运用时会出错。上述例子应修改如下:
int bytesAvailable;
// ...
if (bytesAvailable > 0) {
ProcessData();
bytesAvailable -= n;
} else {
waitForMoreData();
bytesAvailable = n;
}
嵌套if语句
嵌套(nested)if语句是指该if语句为另一个if或者else语句的对象。在编程时经常要用到嵌套if语句。当你使用嵌套if语句时,需记住的要点就是:一个else语句总是对应着和它在同一个块中的最近的if语句,而且该if语句没有与其他else语句相关联。下面是一个例子:
if(i == 10) {
if(j < 20) a = b;
if(k > 100) c = d; // this if is
else a = c; // associated with this else
}
else a = d; // this else refers to if(i == 10)
如注释所示,最后一个else语句没有与if(j <20)相对应,因为它们不在同一个块(尽管if(j <20)语句是没有与else配对最近的if语句)。最后一个else语句对应着if(i==10)。内部的else语句对应着if(k>100),因为它是同一个块中最近的if语句。
if-else-if阶梯
基于嵌套if语句的通用编程结构被称为 if-else-if 阶梯。它的语法如下:
if(condition)
statement;
else if(condition)
statement;
else if(condition)
statement;
.
.
.
else
statement;
条件表达式从上到下被求值。一旦找到为真的条件,就执行与它关联的语句,该阶梯的其他部分就被忽略了。如果所有的条件都不为真,则执行最后的else语句。最后的else语句经常被作为默认的条件,即如果所有其他条件测试失败,就执行最后的else语句。如果没有最后的else语句,而且所有其他的条件都失败,那程序就不做任何动作。
下面的程序通过使用if-else-if阶梯来确定某个月是什么季节。
// Demonstrate if-else-if statements.
class IfElse {
public static void main(String args[]) {
int month = 4; // April
String season;
if(month == 12 || month == 1 || month == 2)
season = "Winter";
else if(month == 3 || month == 4 || month == 5)
season = "Spring";
else if(month == 6 || month == 7 || month == 8)
season = "Summer";
else if(month == 9 || month == 10 || month == 11)
season = "Autumn";
else
season = "Bogus Month";
System.out.println("April is in the " + season + ".");
}
}
该程序产生如下输出:
April is in the Spring.
在往下继续讲之前,你可能想要先试验这个程序。你将看到,不管你给month什么值,该阶梯中有而且只有一个语句执行。
5.1.2switch语句
switch语句是Java的多路分支语句。它提供了一种基于一个表达式的值来使程序执行不同部分的简单方法。因此,它提供了一个比一系列if-else-if语句更好的选择。switch语句的通用形式如下:
switch (expression) {
case value1:
// statement sequence
break;
case value2:
// statement sequence
break;
.
.
.
case valueN:
// statement sequence
break;
default:
// default statement sequence
}
表达式expression必须为byte,short,int或char类型。每个case语句后的值value必须是与表达式类型兼容的特定的一个常量(它必须为一个常量,而不是变量)。重复的case值是不允许的。
switch语句的执行过程如下:表达式的值与每个case语句中的常量作比较。如果发现了一个与之相匹配的,则执行该case语句后的代码。如果没有一个case常量与表达式的值相匹配,则执行default语句。当然,default语句是可选的。如果没有相匹配的case语句,也没有default语句,则什么也不执行。
在case语句序列中的break语句将引起程序流从整个switch语句退出。当遇到一个break语句时,程序将从整个switch语句后的第一行代码开始继续执行。这有一种“跳出” switch语句的效果。
下面是一个使用switch语句的简单例子:
// A simple example of the switch.
class SampleSwitch {
public static void main(String args[]) {
for(int i=0; i<6; i++)
switch(i) {
case 0:
System.out.println("i is zero.");
break;
case 1:
System.out.println("i is one.");
break;
case 2:
System.out.println("i is two.");
break;
case 3:
System.out.println("i is three.");
break;
default:
System.out.println("i is greater than 3.");
}
}
}
该程序的输出如下:
i is zero.
i is one.
i is two.
i is three.
i is greater than 3.
i is greater than 3.
从中可以看出,每一次循环,与i值相配的case常量后的相关语句就被执行。其他语句则被忽略。当i大于3时,没有可以匹配的case语句,因此执行default语句。
break语句是可选的。如果你省略了break语句,程序将继续执行下一个case语句。有时需要在多个case语句之间没有break语句。例如下面的程序:
// In a switch, break statements are optional.
class MissingBreak {
public static void main(String args[]) {
for(int i=0; i<12; i++)
switch(i) {
case 0:
case 1:
case 2:
case 3:
case 4:
System.out.println("i is less than 5");
break;
case 5:
case 6:
case 7:
case 8:
case 9:
System.out.println("i is less than 10");
break;
default:
System.out.println("i is 10 or more");
}
}
}
该程序产生的输出如下:
i is less than 5
i is less than 5
i is less than 5
i is less than 5
i is less than 5
i is less than 10
i is less than 10
i is less than 10
i is less than 10
i is less than 10
i is 10 or more
i is 10 or more
正如该程序所演示的那样,如果没有break语句,程序将继续执行下面的每一个case语句,直到遇到break语句(或switch语句的末尾)。
当然该例子是为了示例而人为构造的,省略break语句在真实的程序中有许多实际的应用。为了说明它更现实的用法,让我们考虑下例对以前显示季节例子的重写。这个重写的版本使用switch语句来使程序的执行更高效。
// An improved version of the season program.
class Switch {
public static void main(String args[]) {
int month = 4;
String season;
switch (month) {
case 12:
case 1:
case 2:
season = "Winter";
break;
case 3:
case 4:
case 5:
season = "Spring";
break;
case 6:
case 7:
case 8:
season = "Summer";
break;
case 9:
case 10:
case 11:
season = "Autumn";
break;
default:
season = "Bogus Month";
}
System.out.println("April is in the " + season + ".");
}
}
嵌套switch语句
可以将一个switch语句作为一个外部switch语句的语句序列的一部分,这称为嵌套switch语句。因为一个switch语句定义了自己的块,外部switch语句和内部switch语句的case常量不会产生冲突。例如,下面的程序段是完全正确的:
switch(count) {
case 1:
switch(target) { // nested switch
case 0:
System.out.println("target is zero");
break;
case 1: // no conflicts with outer switch
System.out.println("target is one");
break;
}
break;
case 2: // ...
本例中,内部switch语句中的 case 1:语句与外部switch语句中的case 1:语句不冲突。变量count仅与外层的case语句相比较。如果变量count为1,则变量target与内层的case语句相比较。
概括起来说,switch语句有3个重要的特性需注意:
· switch语句不同于if语句的是switch语句仅能测试相等的情况,而if语句可计算任何类型的布尔表达式。也就是switch语句只能寻找case常量间某个值与表达式的值相匹配。
· 在同一个switch语句中没有两个相同的case常量。当然,外部switch语句中的case常量可以和内部switch语句中的case常量相同。
· switch语句通常比一系列嵌套if语句更有效。
最后一点尤其有趣,因为它使我们知道Java编译器如何工作。当编译一个switch语句时,Java编译器将检查每个case常量并且创造一个“跳转表”,这个表将用来在表达式值的基础上选择执行路径。因此,如果你需要在一组值中做出选择,switch语句将比与之等效的if-else语句快得多。编译器可以这样做是因为它知道case常量都是同类型的,所要做的只是将它与switch表达式相比较看是否相等。对于一系列的if表达式,编译器就无此功能。
Java的循环语句有for,while和 do-while。这些语句创造了我们通常所称的循环(loops)。你可能知道,一个循环重复执行同一套指令直到一个结束条件出现。你将看到,Java有适合任何编程所需要的循环结构。
5.2.1while语句
while语句是Java最基本的循环语句。当它的控制表达式是真时,while语句重复执行一个语句或语句块。它的通用格式如下:
while(condition) {
// body ofloop
}
条件condition可以是任何布尔表达式。只要条件表达式为真,循环体就被执行。当条件condition为假时,程序控制就传递到循环后面紧跟的语句行。如果只有单个语句需要重复,大括号是不必要的。
下面的while循环从10开始进行减计数,打印出10行“tick”。
// Demonstrate the while loop.
class While {
public static void main(String args[]) {
int n = 10;
while(n > 0) {
System.out.println("tick " + n);
n--;
}
}
}
当你运行这个程序,它将“tick”10次:
tick 10
tick 9
tick 8
tick 7
tick 6
tick 5
tick 4
tick 3
tick 2
tick 1
因为while语句在循环一开始就计算条件表达式,若开始时条件为假,则循环体一次也不会执行。例如,下面的程序中,对println()的调用从未被执行过:
int a = 10, b = 20;
while(a > b)
System.out.println("This will not be displayed");
while循环(或Java的其他任何循环)的循环体可以为空。这是因为一个空语句(null statement)(仅由一个分号组成的语句)在Java的语法上是合法的。例如,下面的程序:
// The target of a loop can be empty.
class NoBody {
public static void main(String args[]) {
int i, j;
i = 100;
j = 200;
// find midpoint between i and j
while(++i < --j) ; // no body in this loop
System.out.println("Midpoint is " + i);
}
}
该程序找出变量i和变量j的中间点。它产生的输出如下:
Midpoint is 150
该程序中的while循环是这样执行的。值i自增,而值j自减,然后比较这两个值。如果新的值i仍比新
的值j小,则进行循环。如果i等于或大于j,则循环停止。在退出循环前,i将保存原始i和j的中间值(当然,这个程序只有在开始时i比j小的情况下才执行)。正如你看到的,这里不需要循环体。所有的行为都出现在条件表达式自身内部。在专业化的Java代码中,一些可以由控制表达式本身处理的短循环通常都没有循环体。
5.2.2do-while循环
如你刚才所见,如果while循环一开始条件表达式就是假的,那么循环体就根本不被执行。然而,有时需要在开始时条件表达式即使是假的情况下,while循环至少也要执行一次。换句话说,有时你需要在一次循环结束后再测试中止表达式,而不是在循环开始时。幸运的是,Java就提供了这样的循环:do-while循环。do-while循环总是执行它的循环体至少一次,因为它的条件表达式在循环的结尾。它的通用格式如下:
do {
// body of loop
} while (condition);
do-while循环总是先执行循环体,然后再计算条件表达式。如果表达式为真,则循环继续。否则,循环结束。对所有的Java循环都一样,条件condition必须是一个布尔表达式。
下面是一个重写的“tick”程序,用来演示do-while循环。它的输出与先前程序的输出相同。
// Demonstrate the do-while loop.
class DoWhile {
public static void main(String args[]) {
int n = 10;
do {
System.out.println("tick " + n);
n--;
} while(n > 0);
}
}
该程序中的循环虽然在技术上是正确的,但可以像如下这样编写更为高效:
do {
System.out.println("tick " + n);
} while(--n > 0);
在本例中,表达式“-- n > 0 将n值的递减与测试n是否为0组合在一个表达式中。它的执行过程是这样的。首先,执行-- n 语句,将变量n递减,然后返回n的新值。这个值再与0比较,如果比0大,则循环继续。否则结束。
do-while循环在你编制菜单选择时尤为有用,因为通常都想让菜单循环体至少执行一次。下面的程序是一个实现Java选择和重复语句的很简单的帮助系统:
// Using a do-while to process a menu selection
class Menu {
public static void main(String args[])
throws java.io.IOException {
char choice;
do {
System.out.println("Help on:");
System.out.println(" 1. if");
System.out.println(" 2. switch");
System.out.println(" 3. while");
System.out.println(" 4. do-while");
System.out.println(" 5. for/n");
System.out.println("Choose one:");
choice = (char) System.in.read();
} while( choice < '1' || choice > '5');
System.out.println("/n");
switch(choice) {
case '1':
System.out.println("The if:/n");
System.out.println("if(condition) statement;");
System.out.println("else statement;");
break;
case '2':
System.out.println("The switch:/n");
System.out.println("switch(expression) {");
System.out.println(" case constant:");
System.out.println(" statement sequence");
System.out.println(" break;");
System.out.println(" // ...");
System.out.println("}");
break;
case '3':
System.out.println("The while:/n");
System.out.println("while(condition) statement;");
break;
case '4':
System.out.println("The do-while:/n");
System.out.println("do {");
System.out.println(" statement;");
System.out.println("} while (condition);");
break;
case '5':
System.out.println("The for:/n");
System.out.print("for(init; condition; iteration)");
System.out.println(" statement;");
break;
}
}
}
下面是这个程序执行的一个样本输出:
Help on:
1. if
2. switch
3. while
4. do-while
5. for
Choose one:
4
The do-while:
do {
statement;
} while (condition);
在程序中,do-while循环用来验证用户是否输入了有效的选择。如果没有,则要求用户重新输入。因为菜单至少要显示一次,do-while循环是完成此任务的合适语句。
关于此例的其他几点:注意从键盘输入字符通过调用System.in.read( )来读入。这是一个Java 的控制台输入函数。尽管Java的终端 I/O (输入/输出)方法将在第12章中详细讨论,在这里使用 System.in.read ( )来读入用户的选择。它从标准的输入读取字符(返回整数,因此将返回值choice定义为字符型)。默认地,标准输入是按行进入缓冲区的,因此在你输入的任何字符被送到你的程序以前,必须按回车键。
Java的终端输入功能相当有限且不好使用。进一步说,大多数现实的Java程序和applets(小应用程序)都具有图形界面并且是基于窗口的。因此,这本书使用终端的输入并不多。然而,它在本例中是有用的。另外一点:因为使用System.in.read ( ),程序必须指定throws java.io.IOException子句。这行代码对于处理输入错误是必要的。这是Java的异常处理的一部分,将在第10章讨论。
5.2.3 for循环
在第2章曾使用过一个for循环的简单格式。你将看到,for循环是一个功能强大且形式灵活的结构。下面是for循环的通用格式:
for(initialization; condition; iteration) {
// body
}
如只有一条语句需要重复,大括号就没有必要。
for循环的执行过程如下。第一步,当循环启动时,先执行其初始化部分。通常,这是设置循环控制变量值的一个表达式,作为控制循环的计数器。重要的是你要理解初始化表达式仅被执行一次。下一步,计算条件condition的值。条件condition必须是布尔表达式。它通常将循环控制变量与目标值相比较。如果这个表达式为真,则执行循环体;如果为假,则循环终止。再下一步执行循环体的反复部分。这部分通常是增加或减少循环控制变量的一个表达式。接下来重复循环,首先计算条件表达式的值,然后执行循环体,接着执行反复表达式。这个过程不断重复直到控制表达式变为假。
下面是使用for循环的“tick”程序:
// Demonstrate the for loop.
class ForTick {
public static void main(String args[]) {
int n;
for(n=10; n>0; n--)
System.out.println("tick " + n);
}
}
在for循环中声明循环控制变量
控制for循环的变量经常只是用于该循环,而不用在程序的其他地方。在这种情况下,可以在循环的初始化部分中声明变量。例如,下面重写了前面的程序,使变量 n 在for循环中被声明为整型:
// Declare a loop control variable inside the for.
class ForTick {
public static void main(String args[]) {
// here, n is declared inside of the for loop
for(int n=10; n>0; n--)
System.out.println("tick " + n);
}
}
当你在for循环内声明变量时,必须记住重要的一点:该变量的作用域在for语句执行后就结束了(因此,该变量的作用域就局限于for循环内)。在for循环外,变量就不存在了。如果你在程序的其他地方需要使用循环控制变量,你就不能在for循环中声明它。
由于循环控制变量不会在程序的其他地方使用,大多数程序员都在for循环中来声明它。例如,以下为测试素数的一个简单程序。注意由于其他地方不需要i,所以循环控制变量i在for循环中声明。
// Test for primes.
class FindPrime {
public static void main(String args[]) {
int num;
boolean isPrime = true;
num = 14;
for(int i=2; i <= num/2; i++) {
if((num % i) == 0) {
isPrime = false;
break;
}
}
if(isPrime) System.out.println("Prime");
else System.out.println("Not Prime");
}
}
使用逗号
你可能经常需要在初始化和for循环的反复部分包括超过一个变量的声明。例如,考虑下面程序的循环部分:
class Sample {
public static void main(String args[]) {
int a, b;
b = 4;
for(a=1; a<b; a++) {
System.out.println("a = " + a);
System.out.println("b = " + b);
b--;
}
}
}
如你所看到的,循环被两个相互作用的变量控制。由于循环被两个变量控制,如果两个变量都能被定义在for循环中,而变量b不需要通过人工处理将是很有用的。幸好,Java提供了一个完成此任务的方法。为了允许两个或两个以上的变量控制循环,Java允许你在for循环的初始化部分和反复部分声明多个变量,每个变量之间用逗号分开。
使用逗号,前面的for循环将更高效,改写后的程序如下:
// Using the comma.
class Comma {
public static void main(String args[]) {
int a, b;
for(a=1, b=4; a<b; a++, b--) {
System.out.println("a = " + a);
System.out.println("b = " + b);
}
}
}
在本例中,初始化部分把两个变量a和 b都定义了。在循环的反复部分,用两个逗号分开的语句在每次循环重复时都执行。程序输出如下:
a = 1
b = 4
a = 2
b = 3
注意:如果你对C/C++熟悉,你就会知道逗号是一个运算符,能在任何有效的表达式中使用。然而,在Java 中不是这样。在 Java中 ,逗号仅仅是一个分隔符,只适用于for循环。
5.2.4for循环的一些变化
for循环支持一些变化,这增加了它的功能和灵活性。for循环这样灵活是因为它的3部分(初始化部分,条件测试部分和反复部分)并不仅用于它们所限定的那些目的。事实上,for 循环的3部分能被用于你需要的任何目的。让我们看一些例子。
最普通的变化之一包含在条件表达式中。具体地说,条件表达式可以不需要用循环变量和目标值的比较来测试循环条件。事实上,控制for循环的条件可以是任何布尔表达式。例如,考虑下列程序片段:
boolean done = false;
for(int i=1; !done; i++) {
// ...
if(interrupted()) done = true;
}
在本例中,for循环将一直运行,直到布尔型变量done被设置为真。for循环的条件部分不测试值i。
下面是for循环的另外一个有趣的变化。在Java中可以使for循环的初始化、条件或者反复部分中的任何或者全部都为空,如下面的程序:
// Parts of the for loop can be empty.
class ForVar {
public static void main(String args[]) {
int i;
boolean done = false;
i = 0;
for( ; !done; ) {
System.out.println("i is " + i);
if(i == 10) done = true;
i++;
}
}
}
本例中,初始化部分和反复部分被移到了for循环以外。这样,for循环的初始化部分和反复部分是空的。当这个简单的例子中,for循环中没有值,确实,这种风格被认为是相当差的,有时这种风格也是有用的。例如,如果初始条件由程序中其他部分的复杂表达式来定义,或者循环控制变量的改变由发生在循环体内的行为决定,而且这种改变是一种非顺序的方式,这种情况下,可以使for循环的这些部分为空。
下面是for循环变化的又一种方式。如果for循环的三个部分全为空,你就可以创建一个无限循环(从来不停止的循环)。例如:
for( ; ; ) {
// ...
}
这个循环将始终运行,因为没有使它终止的条件。尽管有一些程序,例如操作系统命令处理器,需要无限循环,但大多数“无限循环”实际上是具有特殊终止要求的循环。在不久你将看到如何不用正常的条件表达式来终止这种类型的循环。
5.2.5循环嵌套
和其他编程语言一样,Java允许循环嵌套。也就是,一个循环在另一个循环之内。例如,下面的程序就是循环嵌套:
// Loops may be nested.
class Nested {
public static void main(String args[]) {
int i, j;
for(i=0; i<10; i++) {
for(j=i; j<10; j++)
System.out.print(".");
System.out.println();
}
}
}
该程序产生的输出如下所示:
. . . . . . . . . .
. . . . . . . . .
. . . . . . . .
. . . . . . .
. . . . . .
. . . . .
. . . .
. . .
. .
.
Java 支持 3 种跳转语句:break,continue和return。这些语句把控制转移到程序的其他部分。下面对每一种语句进行讨论。
注意:除了这里讨论的跳转语句,Java还支持另一种能改变你程序执行流程的方法:通过异常处理。异常处理提供了一种结构化的方法,通过该方法可以使你的程序捕获并处理运行时刻错误。它由下列五个关键字来控制:try,catch,throw,throws,和 finally。实质上,异常处理机制允许你的程序完成一个非局部的分支跳转。由于异常处理是一个大话题,我们将在第 10 章专门讨论。
5.3.1使用break语句
在Java中,break语句有3种作用。第一,你已经看到,在switch语句中,它被用来终止一个语句序列。第二,它能被用来退出一个循环。第三,它能作为一种“先进”的goto 语句来使用。下面对最后 2种用法进行解释。
使用break退出循环
可以使用break语句直接强行退出循环,忽略循环体中的任何其他语句和循环的条件测试。在循环中遇到break语句时,循环被终止,程序控制在循环后面的语句重新开始。下面是一个简单的例子:
// Using break to exit a loop.
class BreakLoop {
public static void main(String args[]) {
for(int i=0; i<100; i++) {
if(i == 10) break; // terminate loop if i is 10
System.out.println("i: " + i);
}
System.out.println("Loop complete.");
}
}
该程序产生如下的输出:
i: 0
i: 1
i: 2
i: 3
i: 4
i: 5
i: 6
i: 7
i: 8
i: 9
Loop complete.
正如你看到的那样,尽管for循环被设计为从 0执行到99,但是当i等于10时,break语句终止了程序。
break语句能用于任何 Java循环中,包括人们有意设置的无限循环。例如,将上一个程序用while循环改写如下。该程序的输出和刚才看到的输出一样。
// Using break to exit a while loop.
class BreakLoop2 {
public static void main(String args[]) {
int i = 0;
while(i < 100) {
if(i == 10) break; // terminate loop if i is 10
System.out.println("i: " + i);
i++;
}
System.out.println("Loop complete.");
}
}
在一系列嵌套循环中使用break语句时,它将仅仅终止最里面的循环。例如:
// Using break with nested loops.
class BreakLoop3 {
public static void main(String args[]) {
for(int i=0; i<3; i++) {
System.out.print("Pass " + i + ": ");
for(int j=0; j<100; j++) {
if(j == 10) break; // terminate loop if j is 10
System.out.print(j + " ");
}
System.out.println();
}
System.out.println("Loops complete.");
}
}
该程序产生如下的输出:
Pass 0: 0 1 2 3 4 5 6 7 8 9
Pass 1: 0 1 2 3 4 5 6 7 8 9
Pass 2: 0 1 2 3 4 5 6 7 8 9
Loops complete.
从中可以看出,在内部循环中的break语句仅仅终止了该循环,外部的循环不受影响。
关于break,在这里要记住两点。首先,一个循环中可以有一个以上的break语句。但要小心,太多的break语句会破坏你的代码结构。其次,switch语句中的break仅仅影响该switch语句,而不会影响其中的任何循环。
注意:break不是被设计来提供一种正常的循环终止的方法。循环的条件语句是专门用来终止循环的。只有在某类特殊的情况下,才用break语句来取消一个循环。
把break当作goto的一种形式来用
break语句除了在switch语句和循环中使用之外,它还能作为goto 语句的一种“文明”形式来使用。Java中没有 goto语句,因为goto语句提供了一种改变程序运行流程的非结构化方式。这通常使程序难以理解和难于维护。它也阻止了某些编译器的优化。但是,有些地方goto语句对于构造流程控制是有用的而且是合法的。例如,从嵌套很深的循环中退出时, goto语句就很有帮助。因此,Java定义了break语句的一种扩展形式来处理这种情况。通过使用这种形式的break,你可以终止一个或者几个代码块。这些代码块不必是一个循环或一个switch语句的一部分,它们可以是任何的块。而且,由于这种形式的break语句带有标签,你可以明确指定执行从何处重新开始。你将看到,break带给你的是goto的益处,并舍弃了goto语句带来的麻烦。
标签break语句的通用格式如下所示:
break label;
这里,标签label是标识代码块的标签。当这种形式的break执行时,控制被传递出指定的代码块。被加标签的代码块必须包围break语句,但是它不需要是直接的包围break的块。这意味着你可以使用一个加标签的break语句退出一系列的嵌套块。但是你不能使用break语句将控制传递到不包含break语句的代码块。
要指定一个代码块,在其开头加一个标签即可。标签(label)可以是任何合法有效的Java标识符后跟一个冒号。一旦你给一个块加上标签后,你就可以使用这个标签作为break语句的对象了。这样做会使执行在加标签的块的结尾重新开始。例如,下面的程序示例了 3 个嵌套块,每一个都有它自己的标签。break语句使执行向前跳,调过了定义为标签second的代码块结尾,跳过了2个 println ( )语句。
// Using break as a civilized form of goto.
class Break {
public static void main(String args[]) {
boolean t = true;
first: {
second: {
third: {
System.out.println("Before the break.");
if(t) break second; // break out of second block
System.out.println("This won't execute");
}
System.out.println("This won't execute");
}
System.out.println("This is after second block.");
}
}
}
运行该程序,产生如下的输出:
Before the break.
This is after second block.
标签break语句的一个最普遍的用法是退出循环嵌套。例如,下面的程序中,外层的循环只执行了一次:
// Using break to exit from nested loops
class BreakLoop4 {
public static void main(String args[]) {
outer: for(int i=0; i<3; i++) {
System.out.print("Pass " + i + ": ");
for(int j=0; j<100; j++) {
if(j == 10) break outer; // exit both loops
System.out.print(j + " ");
}
System.out.println("This will not print");
}
System.out.println("Loops complete.");
}
}
该程序产生如下的输出:
Pass 0: 0 1 2 3 4 5 6 7 8 9 Loops complete.
你可以看到,当内部循环退到外部循环时,两个循环都被终止了。
记住如果一个标签不在包围break的块中定义,你就不能break到该标签。例如,下面的程序就是非法的,且不会被编译:
// This program contains an error.
class BreakErr {
public static void main(String args[]) {
one: for(int i=0; i<3; i++) {
System.out.print("Pass " + i + ": ");
}
for(int j=0; j<100; j++) {
if(j == 10) break one; // WRONG
System.out.print(j + " ");
}
}
}
因为标签为one的循环没有包围break语句,所以不能将控制传递到该块。
5.3.2使用continue语句
有时强迫一个循环提早反复是有用的。也就是,你可能想要继续运行循环,但是要忽略这次重复剩余的循环体的语句。实际上,goto只不过是跳过循环体,到达循环的尾部。continue语句是break语句的补充。在while和do while循环中,continue语句使控制直接转移给控制循环的条件表达式,然后继续循环过程。在for循环中,循环的反复表达式被求值,然后执行条件表达式,循环继续执行。对于这3种循环,任何中间的代码将被旁路。
下例使用continue语句,使每行打印2个数字:
// Demonstrate continue.
class Continue {
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("");
}
}
}
该程序使用%(模)运算符来检验变量i是否为偶数,如果是,循环继续执行而不输出一个新行。该程序的结果如下:
0 1
2 3
4 5
6 7
8 9
对于break语句,continue可以指定一个标签来说明继续哪个包围的循环。下面的例子运用continue语句来打印0到9的三角形乘法表:
// Using continue with a label.
class ContinueLabel {
public static void main(String args[]) {
outer: for (int i=0; i<10; i++) {
for(int j=0; j<10; j++) {
if(j > i) {
System.out.println();
continue outer;
}
System.out.print(" " + (i * j));
}
}
System.out.println();
}
}
在本例中的continue语句终止了计数j的循环而继续计数i的下一次循环反复。该程序的输出如下:
0
0 1
0 2 4
0 3 6 9
0 4 8 12 16
0 5 10 15 20 25
0 6 12 18 24 30 36
0 7 14 21 28 35 42 49
0 8 16 24 32 40 48 56 64
0 9 18 27 36 45 54 63 72 81
很好的利用continue语句的情况很少,一个原因是Java提供了一系列丰富的循环语句,可以适用于绝大多数应用程序。但是,对于那些需要提早反复的特殊情形,continue语句提供了一个结构化的方法来实现。
5.3.3使用return语句
最后一个控制语句是return。return语句用来明确地从一个方法返回。也就是,return语句使程序控制返回到调用它的方法。因此,将它分类为跳转语句。尽管对return语句的详细讨论在第 7 章开始,这里对其作简要地介绍。
在一个方法的任何时间,return语句可被用来使正在执行的分支程序返回到调用它的方法。下面的例子说明这一点。下例中,由于是Java 运行系统调用main(),因此,return语句使程序执行返回到Java运行系统。
// Demonstrate return.
class Return {
public static void main(String args[]) {
boolean t = true;
System.out.println("Before the return.");
if(t) return; // return to caller
System.out.println("This won't execute.");
}
}
该程序的结果如下:
Before the return.
正如你看到的一样,最后的println( )语句没有被执行。一旦return语句被执行,程序控制传递到它的调用者。
最后一点:在上面的程序中,if(t)语句是必要的。没有它,Java编译器将标记“执行不到的代码”(unreachable code)错误,因为编译器知道最后的println()语句将永远不会被执行。为阻止这个错误,为了这个例子能够执行,在这里使用if语句来“蒙骗”编译器。
编程语言使用控制(control)语句来产生执行流,从而完成程序状态的改变,如程序顺序执行和分支执行。Java的程序控制语句分为以下几类:选择,重复和跳转。根据表达式结果或变量状态选择(Selection)语句来使你的程序选择不同的执行路径。重复(Iteration)语句使程序能够重复执行一个或一个以上语句(也就是说,重复语句形成循环)。跳转(Jump)语句允许你的程序以非线性的方式执行。下面将分析Java 的所有控制语句。
如果你熟悉C/C++,那么掌握Java的控制语句将很容易。事实上,Java的控制语句与C/C++中的语句几乎完全相同。当然它们还是有一些差别的——尤其是break语句与continue语句。
Java支持两种选择语句:if语句和switch语句。这些语句允许你只有在程序运行时才能知道其状态的情况下,控制程序的执行过程。如果你没有C/C++的编程背景,你将会为这两个语句的强大功能和灵活性而感到吃惊。
5.1.1 if语句
if语句曾在第2章中介绍过,我们将在这里对它进行详细讨论。if语句是Java中的条件分支语句。它能将程序的执行路径分为两条。if语句的完整格式如下:
if (condition) statement1;
else statement2;
其中,if和else的对象都是单个语句(statement),也可以是程序块。条件condition可以是任何返回布尔值的表达式。else子句是可选的。
if语句的执行过程如下:如果条件为真,就执行if的对象(statement1);否则,执行else的对象(statement2)。任何时候两条语句都不可能同时执行。考虑下面的例子:
int a,b;
// ...
if(a < b) a = 0;
else b = 0;
本例中,如果a小于b,那么a被赋值为0;否则,b被赋值为0。任何情况下都不可能使a和b都被赋值为0。
通常,用于控制if语句的表达式都包含关系运算符。当然,这在技术上并不是必要的。仅用一个布尔值来控制if语句也是可以的,如下面的程序段:
boolean dataAvailable;
// ...
if (dataAvailable)
ProcessData();
else
waitForMoreData();
记住,直接跟在if 或else语句后的语句只能有一句。如果你想包含更多的语句,你需要建一个程序块,如下面的例子:
int bytesAvailable;
// ...
if (bytesAvailable > 0) {
ProcessData();
bytesAvailable -= n;
} else
waitForMoreData();
这里,如果变量bytesAvailable 大于0,则if块内的所有语句都会执行。
一些程序员觉得在使用if语句时在其后跟一个大括号是很方便的,甚至在只有一条语句的时侯也使用大括号。这使得在日后添加别的语句变得容易,并且你也不必担心忘记括号。事实上,当需要定义块时而未对其进行定义是一个导致错误的普遍原因。例如,考虑下面的程序段:
int bytesAvailable;
// ...
if (bytesAvailable > 0) {
ProcessData();
bytesAvailable -= n;
} else
waitForMoreData();
bytesAvailable = n;
由于编排的原因,看起来似乎 bytesAvailable = n语句应该在else子句中执行。然而,当你调用时,空白对Java无关紧要,编译器无法知道你的意图。这段程序会通过编译,但运用时会出错。上述例子应修改如下:
int bytesAvailable;
// ...
if (bytesAvailable > 0) {
ProcessData();
bytesAvailable -= n;
} else {
waitForMoreData();
bytesAvailable = n;
}
嵌套if语句
嵌套(nested)if语句是指该if语句为另一个if或者else语句的对象。在编程时经常要用到嵌套if语句。当你使用嵌套if语句时,需记住的要点就是:一个else语句总是对应着和它在同一个块中的最近的if语句,而且该if语句没有与其他else语句相关联。下面是一个例子:
if(i == 10) {
if(j < 20) a = b;
if(k > 100) c = d; // this if is
else a = c; // associated with this else
}
else a = d; // this else refers to if(i == 10)
如注释所示,最后一个else语句没有与if(j <20)相对应,因为它们不在同一个块(尽管if(j <20)语句是没有与else配对最近的if语句)。最后一个else语句对应着if(i==10)。内部的else语句对应着if(k>100),因为它是同一个块中最近的if语句。
if-else-if阶梯
基于嵌套if语句的通用编程结构被称为 if-else-if 阶梯。它的语法如下:
if(condition)
statement;
else if(condition)
statement;
else if(condition)
statement;
.
.
.
else
statement;
条件表达式从上到下被求值。一旦找到为真的条件,就执行与它关联的语句,该阶梯的其他部分就被忽略了。如果所有的条件都不为真,则执行最后的else语句。最后的else语句经常被作为默认的条件,即如果所有其他条件测试失败,就执行最后的else语句。如果没有最后的else语句,而且所有其他的条件都失败,那程序就不做任何动作。
下面的程序通过使用if-else-if阶梯来确定某个月是什么季节。
// Demonstrate if-else-if statements.
class IfElse {
public static void main(String args[]) {
int month = 4; // April
String season;
if(month == 12 || month == 1 || month == 2)
season = "Winter";
else if(month == 3 || month == 4 || month == 5)
season = "Spring";
else if(month == 6 || month == 7 || month == 8)
season = "Summer";
else if(month == 9 || month == 10 || month == 11)
season = "Autumn";
else
season = "Bogus Month";
System.out.println("April is in the " + season + ".");
}
}
该程序产生如下输出:
April is in the Spring.
在往下继续讲之前,你可能想要先试验这个程序。你将看到,不管你给month什么值,该阶梯中有而且只有一个语句执行。
5.1.2switch语句
switch语句是Java的多路分支语句。它提供了一种基于一个表达式的值来使程序执行不同部分的简单方法。因此,它提供了一个比一系列if-else-if语句更好的选择。switch语句的通用形式如下:
switch (expression) {
case value1:
// statement sequence
break;
case value2:
// statement sequence
break;
.
.
.
case valueN:
// statement sequence
break;
default:
// default statement sequence
}
表达式expression必须为byte,short,int或char类型。每个case语句后的值value必须是与表达式类型兼容的特定的一个常量(它必须为一个常量,而不是变量)。重复的case值是不允许的。
switch语句的执行过程如下:表达式的值与每个case语句中的常量作比较。如果发现了一个与之相匹配的,则执行该case语句后的代码。如果没有一个case常量与表达式的值相匹配,则执行default语句。当然,default语句是可选的。如果没有相匹配的case语句,也没有default语句,则什么也不执行。
在case语句序列中的break语句将引起程序流从整个switch语句退出。当遇到一个break语句时,程序将从整个switch语句后的第一行代码开始继续执行。这有一种“跳出” switch语句的效果。
下面是一个使用switch语句的简单例子:
// A simple example of the switch.
class SampleSwitch {
public static void main(String args[]) {
for(int i=0; i<6; i++)
switch(i) {
case 0:
System.out.println("i is zero.");
break;
case 1:
System.out.println("i is one.");
break;
case 2:
System.out.println("i is two.");
break;
case 3:
System.out.println("i is three.");
break;
default:
System.out.println("i is greater than 3.");
}
}
}
该程序的输出如下:
i is zero.
i is one.
i is two.
i is three.
i is greater than 3.
i is greater than 3.
从中可以看出,每一次循环,与i值相配的case常量后的相关语句就被执行。其他语句则被忽略。当i大于3时,没有可以匹配的case语句,因此执行default语句。
break语句是可选的。如果你省略了break语句,程序将继续执行下一个case语句。有时需要在多个case语句之间没有break语句。例如下面的程序:
// In a switch, break statements are optional.
class MissingBreak {
public static void main(String args[]) {
for(int i=0; i<12; i++)
switch(i) {
case 0:
case 1:
case 2:
case 3:
case 4:
System.out.println("i is less than 5");
break;
case 5:
case 6:
case 7:
case 8:
case 9:
System.out.println("i is less than 10");
break;
default:
System.out.println("i is 10 or more");
}
}
}
该程序产生的输出如下:
i is less than 5
i is less than 5
i is less than 5
i is less than 5
i is less than 5
i is less than 10
i is less than 10
i is less than 10
i is less than 10
i is less than 10
i is 10 or more
i is 10 or more
正如该程序所演示的那样,如果没有break语句,程序将继续执行下面的每一个case语句,直到遇到break语句(或switch语句的末尾)。
当然该例子是为了示例而人为构造的,省略break语句在真实的程序中有许多实际的应用。为了说明它更现实的用法,让我们考虑下例对以前显示季节例子的重写。这个重写的版本使用switch语句来使程序的执行更高效。
// An improved version of the season program.
class Switch {
public static void main(String args[]) {
int month = 4;
String season;
switch (month) {
case 12:
case 1:
case 2:
season = "Winter";
break;
case 3:
case 4:
case 5:
season = "Spring";
break;
case 6:
case 7:
case 8:
season = "Summer";
break;
case 9:
case 10:
case 11:
season = "Autumn";
break;
default:
season = "Bogus Month";
}
System.out.println("April is in the " + season + ".");
}
}
嵌套switch语句
可以将一个switch语句作为一个外部switch语句的语句序列的一部分,这称为嵌套switch语句。因为一个switch语句定义了自己的块,外部switch语句和内部switch语句的case常量不会产生冲突。例如,下面的程序段是完全正确的:
switch(count) {
case 1:
switch(target) { // nested switch
case 0:
System.out.println("target is zero");
break;
case 1: // no conflicts with outer switch
System.out.println("target is one");
break;
}
break;
case 2: // ...
本例中,内部switch语句中的 case 1:语句与外部switch语句中的case 1:语句不冲突。变量count仅与外层的case语句相比较。如果变量count为1,则变量target与内层的case语句相比较。
概括起来说,switch语句有3个重要的特性需注意:
· switch语句不同于if语句的是switch语句仅能测试相等的情况,而if语句可计算任何类型的布尔表达式。也就是switch语句只能寻找case常量间某个值与表达式的值相匹配。
· 在同一个switch语句中没有两个相同的case常量。当然,外部switch语句中的case常量可以和内部switch语句中的case常量相同。
· switch语句通常比一系列嵌套if语句更有效。
最后一点尤其有趣,因为它使我们知道Java编译器如何工作。当编译一个switch语句时,Java编译器将检查每个case常量并且创造一个“跳转表”,这个表将用来在表达式值的基础上选择执行路径。因此,如果你需要在一组值中做出选择,switch语句将比与之等效的if-else语句快得多。编译器可以这样做是因为它知道case常量都是同类型的,所要做的只是将它与switch表达式相比较看是否相等。对于一系列的if表达式,编译器就无此功能。
Java的循环语句有for,while和 do-while。这些语句创造了我们通常所称的循环(loops)。你可能知道,一个循环重复执行同一套指令直到一个结束条件出现。你将看到,Java有适合任何编程所需要的循环结构。
5.2.1while语句
while语句是Java最基本的循环语句。当它的控制表达式是真时,while语句重复执行一个语句或语句块。它的通用格式如下:
while(condition) {
// body ofloop
}
条件condition可以是任何布尔表达式。只要条件表达式为真,循环体就被执行。当条件condition为假时,程序控制就传递到循环后面紧跟的语句行。如果只有单个语句需要重复,大括号是不必要的。
下面的while循环从10开始进行减计数,打印出10行“tick”。
// Demonstrate the while loop.
class While {
public static void main(String args[]) {
int n = 10;
while(n > 0) {
System.out.println("tick " + n);
n--;
}
}
}
当你运行这个程序,它将“tick”10次:
tick 10
tick 9
tick 8
tick 7
tick 6
tick 5
tick 4
tick 3
tick 2
tick 1
因为while语句在循环一开始就计算条件表达式,若开始时条件为假,则循环体一次也不会执行。例如,下面的程序中,对println()的调用从未被执行过:
int a = 10, b = 20;
while(a > b)
System.out.println("This will not be displayed");
while循环(或Java的其他任何循环)的循环体可以为空。这是因为一个空语句(null statement)(仅由一个分号组成的语句)在Java的语法上是合法的。例如,下面的程序:
// The target of a loop can be empty.
class NoBody {
public static void main(String args[]) {
int i, j;
i = 100;
j = 200;
// find midpoint between i and j
while(++i < --j) ; // no body in this loop
System.out.println("Midpoint is " + i);
}
}
该程序找出变量i和变量j的中间点。它产生的输出如下:
Midpoint is 150
该程序中的while循环是这样执行的。值i自增,而值j自减,然后比较这两个值。如果新的值i仍比新
的值j小,则进行循环。如果i等于或大于j,则循环停止。在退出循环前,i将保存原始i和j的中间值(当然,这个程序只有在开始时i比j小的情况下才执行)。正如你看到的,这里不需要循环体。所有的行为都出现在条件表达式自身内部。在专业化的Java代码中,一些可以由控制表达式本身处理的短循环通常都没有循环体。
5.2.2do-while循环
如你刚才所见,如果while循环一开始条件表达式就是假的,那么循环体就根本不被执行。然而,有时需要在开始时条件表达式即使是假的情况下,while循环至少也要执行一次。换句话说,有时你需要在一次循环结束后再测试中止表达式,而不是在循环开始时。幸运的是,Java就提供了这样的循环:do-while循环。do-while循环总是执行它的循环体至少一次,因为它的条件表达式在循环的结尾。它的通用格式如下:
do {
// body of loop
} while (condition);
do-while循环总是先执行循环体,然后再计算条件表达式。如果表达式为真,则循环继续。否则,循环结束。对所有的Java循环都一样,条件condition必须是一个布尔表达式。
下面是一个重写的“tick”程序,用来演示do-while循环。它的输出与先前程序的输出相同。
// Demonstrate the do-while loop.
class DoWhile {
public static void main(String args[]) {
int n = 10;
do {
System.out.println("tick " + n);
n--;
} while(n > 0);
}
}
该程序中的循环虽然在技术上是正确的,但可以像如下这样编写更为高效:
do {
System.out.println("tick " + n);
} while(--n > 0);
在本例中,表达式“-- n > 0 将n值的递减与测试n是否为0组合在一个表达式中。它的执行过程是这样的。首先,执行-- n 语句,将变量n递减,然后返回n的新值。这个值再与0比较,如果比0大,则循环继续。否则结束。
do-while循环在你编制菜单选择时尤为有用,因为通常都想让菜单循环体至少执行一次。下面的程序是一个实现Java选择和重复语句的很简单的帮助系统:
// Using a do-while to process a menu selection
class Menu {
public static void main(String args[])
throws java.io.IOException {
char choice;
do {
System.out.println("Help on:");
System.out.println(" 1. if");
System.out.println(" 2. switch");
System.out.println(" 3. while");
System.out.println(" 4. do-while");
System.out.println(" 5. for/n");
System.out.println("Choose one:");
choice = (char) System.in.read();
} while( choice < '1' || choice > '5');
System.out.println("/n");
switch(choice) {
case '1':
System.out.println("The if:/n");
System.out.println("if(condition) statement;");
System.out.println("else statement;");
break;
case '2':
System.out.println("The switch:/n");
System.out.println("switch(expression) {");
System.out.println(" case constant:");
System.out.println(" statement sequence");
System.out.println(" break;");
System.out.println(" // ...");
System.out.println("}");
break;
case '3':
System.out.println("The while:/n");
System.out.println("while(condition) statement;");
break;
case '4':
System.out.println("The do-while:/n");
System.out.println("do {");
System.out.println(" statement;");
System.out.println("} while (condition);");
break;
case '5':
System.out.println("The for:/n");
System.out.print("for(init; condition; iteration)");
System.out.println(" statement;");
break;
}
}
}
下面是这个程序执行的一个样本输出:
Help on:
1. if
2. switch
3. while
4. do-while
5. for
Choose one:
4
The do-while:
do {
statement;
} while (condition);
在程序中,do-while循环用来验证用户是否输入了有效的选择。如果没有,则要求用户重新输入。因为菜单至少要显示一次,do-while循环是完成此任务的合适语句。
关于此例的其他几点:注意从键盘输入字符通过调用System.in.read( )来读入。这是一个Java 的控制台输入函数。尽管Java的终端 I/O (输入/输出)方法将在第12章中详细讨论,在这里使用 System.in.read ( )来读入用户的选择。它从标准的输入读取字符(返回整数,因此将返回值choice定义为字符型)。默认地,标准输入是按行进入缓冲区的,因此在你输入的任何字符被送到你的程序以前,必须按回车键。
Java的终端输入功能相当有限且不好使用。进一步说,大多数现实的Java程序和applets(小应用程序)都具有图形界面并且是基于窗口的。因此,这本书使用终端的输入并不多。然而,它在本例中是有用的。另外一点:因为使用System.in.read ( ),程序必须指定throws java.io.IOException子句。这行代码对于处理输入错误是必要的。这是Java的异常处理的一部分,将在第10章讨论。
5.2.3 for循环
在第2章曾使用过一个for循环的简单格式。你将看到,for循环是一个功能强大且形式灵活的结构。下面是for循环的通用格式:
for(initialization; condition; iteration) {
// body
}
如只有一条语句需要重复,大括号就没有必要。
for循环的执行过程如下。第一步,当循环启动时,先执行其初始化部分。通常,这是设置循环控制变量值的一个表达式,作为控制循环的计数器。重要的是你要理解初始化表达式仅被执行一次。下一步,计算条件condition的值。条件condition必须是布尔表达式。它通常将循环控制变量与目标值相比较。如果这个表达式为真,则执行循环体;如果为假,则循环终止。再下一步执行循环体的反复部分。这部分通常是增加或减少循环控制变量的一个表达式。接下来重复循环,首先计算条件表达式的值,然后执行循环体,接着执行反复表达式。这个过程不断重复直到控制表达式变为假。
下面是使用for循环的“tick”程序:
// Demonstrate the for loop.
class ForTick {
public static void main(String args[]) {
int n;
for(n=10; n>0; n--)
System.out.println("tick " + n);
}
}
在for循环中声明循环控制变量
控制for循环的变量经常只是用于该循环,而不用在程序的其他地方。在这种情况下,可以在循环的初始化部分中声明变量。例如,下面重写了前面的程序,使变量 n 在for循环中被声明为整型:
// Declare a loop control variable inside the for.
class ForTick {
public static void main(String args[]) {
// here, n is declared inside of the for loop
for(int n=10; n>0; n--)
System.out.println("tick " + n);
}
}
当你在for循环内声明变量时,必须记住重要的一点:该变量的作用域在for语句执行后就结束了(因此,该变量的作用域就局限于for循环内)。在for循环外,变量就不存在了。如果你在程序的其他地方需要使用循环控制变量,你就不能在for循环中声明它。
由于循环控制变量不会在程序的其他地方使用,大多数程序员都在for循环中来声明它。例如,以下为测试素数的一个简单程序。注意由于其他地方不需要i,所以循环控制变量i在for循环中声明。
// Test for primes.
class FindPrime {
public static void main(String args[]) {
int num;
boolean isPrime = true;
num = 14;
for(int i=2; i <= num/2; i++) {
if((num % i) == 0) {
isPrime = false;
break;
}
}
if(isPrime) System.out.println("Prime");
else System.out.println("Not Prime");
}
}
使用逗号
你可能经常需要在初始化和for循环的反复部分包括超过一个变量的声明。例如,考虑下面程序的循环部分:
class Sample {
public static void main(String args[]) {
int a, b;
b = 4;
for(a=1; a<b; a++) {
System.out.println("a = " + a);
System.out.println("b = " + b);
b--;
}
}
}
如你所看到的,循环被两个相互作用的变量控制。由于循环被两个变量控制,如果两个变量都能被定义在for循环中,而变量b不需要通过人工处理将是很有用的。幸好,Java提供了一个完成此任务的方法。为了允许两个或两个以上的变量控制循环,Java允许你在for循环的初始化部分和反复部分声明多个变量,每个变量之间用逗号分开。
使用逗号,前面的for循环将更高效,改写后的程序如下:
// Using the comma.
class Comma {
public static void main(String args[]) {
int a, b;
for(a=1, b=4; a<b; a++, b--) {
System.out.println("a = " + a);
System.out.println("b = " + b);
}
}
}
在本例中,初始化部分把两个变量a和 b都定义了。在循环的反复部分,用两个逗号分开的语句在每次循环重复时都执行。程序输出如下:
a = 1
b = 4
a = 2
b = 3
注意:如果你对C/C++熟悉,你就会知道逗号是一个运算符,能在任何有效的表达式中使用。然而,在Java 中不是这样。在 Java中 ,逗号仅仅是一个分隔符,只适用于for循环。
5.2.4for循环的一些变化
for循环支持一些变化,这增加了它的功能和灵活性。for循环这样灵活是因为它的3部分(初始化部分,条件测试部分和反复部分)并不仅用于它们所限定的那些目的。事实上,for 循环的3部分能被用于你需要的任何目的。让我们看一些例子。
最普通的变化之一包含在条件表达式中。具体地说,条件表达式可以不需要用循环变量和目标值的比较来测试循环条件。事实上,控制for循环的条件可以是任何布尔表达式。例如,考虑下列程序片段:
boolean done = false;
for(int i=1; !done; i++) {
// ...
if(interrupted()) done = true;
}
在本例中,for循环将一直运行,直到布尔型变量done被设置为真。for循环的条件部分不测试值i。
下面是for循环的另外一个有趣的变化。在Java中可以使for循环的初始化、条件或者反复部分中的任何或者全部都为空,如下面的程序:
// Parts of the for loop can be empty.
class ForVar {
public static void main(String args[]) {
int i;
boolean done = false;
i = 0;
for( ; !done; ) {
System.out.println("i is " + i);
if(i == 10) done = true;
i++;
}
}
}
本例中,初始化部分和反复部分被移到了for循环以外。这样,for循环的初始化部分和反复部分是空的。当这个简单的例子中,for循环中没有值,确实,这种风格被认为是相当差的,有时这种风格也是有用的。例如,如果初始条件由程序中其他部分的复杂表达式来定义,或者循环控制变量的改变由发生在循环体内的行为决定,而且这种改变是一种非顺序的方式,这种情况下,可以使for循环的这些部分为空。
下面是for循环变化的又一种方式。如果for循环的三个部分全为空,你就可以创建一个无限循环(从来不停止的循环)。例如:
for( ; ; ) {
// ...
}
这个循环将始终运行,因为没有使它终止的条件。尽管有一些程序,例如操作系统命令处理器,需要无限循环,但大多数“无限循环”实际上是具有特殊终止要求的循环。在不久你将看到如何不用正常的条件表达式来终止这种类型的循环。
5.2.5循环嵌套
和其他编程语言一样,Java允许循环嵌套。也就是,一个循环在另一个循环之内。例如,下面的程序就是循环嵌套:
// Loops may be nested.
class Nested {
public static void main(String args[]) {
int i, j;
for(i=0; i<10; i++) {
for(j=i; j<10; j++)
System.out.print(".");
System.out.println();
}
}
}
该程序产生的输出如下所示:
. . . . . . . . . .
. . . . . . . . .
. . . . . . . .
. . . . . . .
. . . . . .
. . . . .
. . . .
. . .
. .
.
Java 支持 3 种跳转语句:break,continue和return。这些语句把控制转移到程序的其他部分。下面对每一种语句进行讨论。
注意:除了这里讨论的跳转语句,Java还支持另一种能改变你程序执行流程的方法:通过异常处理。异常处理提供了一种结构化的方法,通过该方法可以使你的程序捕获并处理运行时刻错误。它由下列五个关键字来控制:try,catch,throw,throws,和 finally。实质上,异常处理机制允许你的程序完成一个非局部的分支跳转。由于异常处理是一个大话题,我们将在第 10 章专门讨论。
5.3.1使用break语句
在Java中,break语句有3种作用。第一,你已经看到,在switch语句中,它被用来终止一个语句序列。第二,它能被用来退出一个循环。第三,它能作为一种“先进”的goto 语句来使用。下面对最后 2种用法进行解释。
使用break退出循环
可以使用break语句直接强行退出循环,忽略循环体中的任何其他语句和循环的条件测试。在循环中遇到break语句时,循环被终止,程序控制在循环后面的语句重新开始。下面是一个简单的例子:
// Using break to exit a loop.
class BreakLoop {
public static void main(String args[]) {
for(int i=0; i<100; i++) {
if(i == 10) break; // terminate loop if i is 10
System.out.println("i: " + i);
}
System.out.println("Loop complete.");
}
}
该程序产生如下的输出:
i: 0
i: 1
i: 2
i: 3
i: 4
i: 5
i: 6
i: 7
i: 8
i: 9
Loop complete.
正如你看到的那样,尽管for循环被设计为从 0执行到99,但是当i等于10时,break语句终止了程序。
break语句能用于任何 Java循环中,包括人们有意设置的无限循环。例如,将上一个程序用while循环改写如下。该程序的输出和刚才看到的输出一样。
// Using break to exit a while loop.
class BreakLoop2 {
public static void main(String args[]) {
int i = 0;
while(i < 100) {
if(i == 10) break; // terminate loop if i is 10
System.out.println("i: " + i);
i++;
}
System.out.println("Loop complete.");
}
}
在一系列嵌套循环中使用break语句时,它将仅仅终止最里面的循环。例如:
// Using break with nested loops.
class BreakLoop3 {
public static void main(String args[]) {
for(int i=0; i<3; i++) {
System.out.print("Pass " + i + ": ");
for(int j=0; j<100; j++) {
if(j == 10) break; // terminate loop if j is 10
System.out.print(j + " ");
}
System.out.println();
}
System.out.println("Loops complete.");
}
}
该程序产生如下的输出:
Pass 0: 0 1 2 3 4 5 6 7 8 9
Pass 1: 0 1 2 3 4 5 6 7 8 9
Pass 2: 0 1 2 3 4 5 6 7 8 9
Loops complete.
从中可以看出,在内部循环中的break语句仅仅终止了该循环,外部的循环不受影响。
关于break,在这里要记住两点。首先,一个循环中可以有一个以上的break语句。但要小心,太多的break语句会破坏你的代码结构。其次,switch语句中的break仅仅影响该switch语句,而不会影响其中的任何循环。
注意:break不是被设计来提供一种正常的循环终止的方法。循环的条件语句是专门用来终止循环的。只有在某类特殊的情况下,才用break语句来取消一个循环。
把break当作goto的一种形式来用
break语句除了在switch语句和循环中使用之外,它还能作为goto 语句的一种“文明”形式来使用。Java中没有 goto语句,因为goto语句提供了一种改变程序运行流程的非结构化方式。这通常使程序难以理解和难于维护。它也阻止了某些编译器的优化。但是,有些地方goto语句对于构造流程控制是有用的而且是合法的。例如,从嵌套很深的循环中退出时, goto语句就很有帮助。因此,Java定义了break语句的一种扩展形式来处理这种情况。通过使用这种形式的break,你可以终止一个或者几个代码块。这些代码块不必是一个循环或一个switch语句的一部分,它们可以是任何的块。而且,由于这种形式的break语句带有标签,你可以明确指定执行从何处重新开始。你将看到,break带给你的是goto的益处,并舍弃了goto语句带来的麻烦。
标签break语句的通用格式如下所示:
break label;
这里,标签label是标识代码块的标签。当这种形式的break执行时,控制被传递出指定的代码块。被加标签的代码块必须包围break语句,但是它不需要是直接的包围break的块。这意味着你可以使用一个加标签的break语句退出一系列的嵌套块。但是你不能使用break语句将控制传递到不包含break语句的代码块。
要指定一个代码块,在其开头加一个标签即可。标签(label)可以是任何合法有效的Java标识符后跟一个冒号。一旦你给一个块加上标签后,你就可以使用这个标签作为break语句的对象了。这样做会使执行在加标签的块的结尾重新开始。例如,下面的程序示例了 3 个嵌套块,每一个都有它自己的标签。break语句使执行向前跳,调过了定义为标签second的代码块结尾,跳过了2个 println ( )语句。
// Using break as a civilized form of goto.
class Break {
public static void main(String args[]) {
boolean t = true;
first: {
second: {
third: {
System.out.println("Before the break.");
if(t) break second; // break out of second block
System.out.println("This won't execute");
}
System.out.println("This won't execute");
}
System.out.println("This is after second block.");
}
}
}
运行该程序,产生如下的输出:
Before the break.
This is after second block.
标签break语句的一个最普遍的用法是退出循环嵌套。例如,下面的程序中,外层的循环只执行了一次:
// Using break to exit from nested loops
class BreakLoop4 {
public static void main(String args[]) {
outer: for(int i=0; i<3; i++) {
System.out.print("Pass " + i + ": ");
for(int j=0; j<100; j++) {
if(j == 10) break outer; // exit both loops
System.out.print(j + " ");
}
System.out.println("This will not print");
}
System.out.println("Loops complete.");
}
}
该程序产生如下的输出:
Pass 0: 0 1 2 3 4 5 6 7 8 9 Loops complete.
你可以看到,当内部循环退到外部循环时,两个循环都被终止了。
记住如果一个标签不在包围break的块中定义,你就不能break到该标签。例如,下面的程序就是非法的,且不会被编译:
// This program contains an error.
class BreakErr {
public static void main(String args[]) {
one: for(int i=0; i<3; i++) {
System.out.print("Pass " + i + ": ");
}
for(int j=0; j<100; j++) {
if(j == 10) break one; // WRONG
System.out.print(j + " ");
}
}
}
因为标签为one的循环没有包围break语句,所以不能将控制传递到该块。
5.3.2使用continue语句
有时强迫一个循环提早反复是有用的。也就是,你可能想要继续运行循环,但是要忽略这次重复剩余的循环体的语句。实际上,goto只不过是跳过循环体,到达循环的尾部。continue语句是break语句的补充。在while和do while循环中,continue语句使控制直接转移给控制循环的条件表达式,然后继续循环过程。在for循环中,循环的反复表达式被求值,然后执行条件表达式,循环继续执行。对于这3种循环,任何中间的代码将被旁路。
下例使用continue语句,使每行打印2个数字:
// Demonstrate continue.
class Continue {
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("");
}
}
}
该程序使用%(模)运算符来检验变量i是否为偶数,如果是,循环继续执行而不输出一个新行。该程序的结果如下:
0 1
2 3
4 5
6 7
8 9
对于break语句,continue可以指定一个标签来说明继续哪个包围的循环。下面的例子运用continue语句来打印0到9的三角形乘法表:
// Using continue with a label.
class ContinueLabel {
public static void main(String args[]) {
outer: for (int i=0; i<10; i++) {
for(int j=0; j<10; j++) {
if(j > i) {
System.out.println();
continue outer;
}
System.out.print(" " + (i * j));
}
}
System.out.println();
}
}
在本例中的continue语句终止了计数j的循环而继续计数i的下一次循环反复。该程序的输出如下:
0
0 1
0 2 4
0 3 6 9
0 4 8 12 16
0 5 10 15 20 25
0 6 12 18 24 30 36
0 7 14 21 28 35 42 49
0 8 16 24 32 40 48 56 64
0 9 18 27 36 45 54 63 72 81
很好的利用continue语句的情况很少,一个原因是Java提供了一系列丰富的循环语句,可以适用于绝大多数应用程序。但是,对于那些需要提早反复的特殊情形,continue语句提供了一个结构化的方法来实现。
5.3.3使用return语句
最后一个控制语句是return。return语句用来明确地从一个方法返回。也就是,return语句使程序控制返回到调用它的方法。因此,将它分类为跳转语句。尽管对return语句的详细讨论在第 7 章开始,这里对其作简要地介绍。
在一个方法的任何时间,return语句可被用来使正在执行的分支程序返回到调用它的方法。下面的例子说明这一点。下例中,由于是Java 运行系统调用main(),因此,return语句使程序执行返回到Java运行系统。
// Demonstrate return.
class Return {
public static void main(String args[]) {
boolean t = true;
System.out.println("Before the return.");
if(t) return; // return to caller
System.out.println("This won't execute.");
}
}
该程序的结果如下:
Before the return.
正如你看到的一样,最后的println( )语句没有被执行。一旦return语句被执行,程序控制传递到它的调用者。
最后一点:在上面的程序中,if(t)语句是必要的。没有它,Java编译器将标记“执行不到的代码”(unreachable code)错误,因为编译器知道最后的println()语句将永远不会被执行。为阻止这个错误,为了这个例子能够执行,在这里使用if语句来“蒙骗”编译器。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics