5.1 引言
- 循环可以用于让一个程序反复地执行语句
- Java中提供一种称为循环(loop)的功能亲啊大噶的构造,用来控制一个操作或操作序列重复执行的次数。使用循环语句时,只要简单地告诉计算机输出字符串100次,而无须重复打印输出语句100次,如下所示
int count = 0;while(count <= 100){System.out.println("Welcome to Java!");count++;}
- 变量count的初值为0。循环检测(count < 100)是否为true。若为true,则执行循环体以输出消息”Welcome to Java!”,然后给count加1.重复执行这个循环,直到(count < 100)变为false为止。当(count < 100)变为false(例如:count达到100),此时循环终止,然后执行循环语句之后的下一条语句。
- 循环是用来控制语句块重复执行的一种构造。循环的概念是程序设计的基础。Java提供了三种类型的循环语句:while循环、do - while 循环和for循环。
5.2 while循环
- while循环在条件为真的情况下,重复地执行语句
while 循环的语法如下:while(循环继续条件){//循环体语句(组)}
- 循环继续条件应该总是放在圆括号内。只有当循环体只包含一条语句或不包含语句时,循环体的花括号才可以省略。
- 要保证循环继续条件最终可以变为false,以便程序能够结束。一个常见的程序设计错误是无限循环(也就是说,循环会永远执行下去)。如果你的程序运行了过长的时间而不结束,可能其中就有无限循环。如果你是从命令窗口运行程序的,按
ctrl + c键来结束运行。 - 程序员经常会犯的错误就是使循环多执行一次或少执行一次。这种情况通常称为差一错误(off - by - one error)。例如:下面的循环会将Welcome to Java显示101次,而不是100次。这个错误出在条件部分,条件应该是count < 100而不是count <= 100。
int count = 0;while(count <= 100){System.out.println("Welcome to Java!");count++;}
- 回顾程序清单3-1给出了一个程序,提示用户为两个个位数相加的问题给出答案。现在你可以使用循环重写程序,让用户重复输入新的答案,直到答案正确为止,如下面的程序清单5-1所示。
程序清单 5-1 RepeatAdditionQuiz.java
import java.util.Scanner;public class RepeatAdditionQuiz {public static void main(String[] args) {int number1 = (int) (Math.random() * 10);int number2 = (int) (Math.random() * 10);//Create a ScannerScanner input = new Scanner(System.in);System.out.print("What is " + number1 + " + " + number2 + "?");int answer = input.nextInt();while (number1 + number2 != answer) {System.out.print("Wrong answer .Try again.What is " +number1 + " + " + number2 + " ? ");answer = input.nextInt();}System.out.println("You got it!");}}
5.3 示例学习:猜数字
- 本示例产生一个随机数,然后让用户反复猜测该数字直到答对。
- 要 解决的我呢提是猜测计算机”脑子”里想的是什么数。编写一个程序,随机产生一个0到100之间且包含0和100的整数。程序提示用户连续输入数字,直到它和计算机随机产生的数字相匹配为止。对用户每次输入的数组,程序都提示用户输入值是偏大还是偏小,这样用户可以明智地进行下一轮的猜测。
- 该如何编写这个程序呢?要立即开始编码吗?不!编码前的思考是非常重要的。思考一下,不编写程序你会如何解决这个问题。首先需要产生一个0到100之间且包含0和100的随机数,然后提示用户输入一个猜测数,最后将这个猜测数和随机数进行比较。
- 一次增加一个步骤地渐近编码(code incrementally)是一个很好的习惯。对涉及编写循环的程序而言,如果不知道如何立即编写循环,可以编写循环只执行一次的代码,然后规划如何在循环中重复执行这些代码。为了编写程序,可以打一个初稿,如程序清单5-2所示。
程序清单 5-2 GuessNumberOneTime.java
import java.util.Scanner;public class GuessNumberOneTime {public static void main(String[] args) {//Generate a random number to be guessedint number = (int) (Math.random() * 101);Scanner input = new Scanner(System.in);System.out.println("Guess a magic number between 0 and 100");//Prompt the user to guess the numberSystem.out.print("\nEnter your guess");int guess = input.nextInt();if(guess == number) {System.out.println("Yes, the number is " + number);} else if (guess > number) {System.out.println("Your guess is too high");} else {System.out.println("Your guess is too low");}}}
程序清单 5-3 GuessNumber.java
import java.util.Scanner;public class GuessNumber {public static void main(String[] args) {//Generate a random number to be guessedint number = (int) (Math.random() * 101);Scanner input = new Scanner(System.in);System.out.println("Guess a magic number between 0 and 100");int guess = -1;while (guess != number) {//Prompt the user to guess the numnberSystem.out.print("\nEnter your guess: ");guess = input.nextInt();if (guess == number)System.out.println("Yes,the number is " + number);else if (guess > number)System.out.println("Your guess is too high");elseSystem.out.println("Your guess is too low");}//End of loop}}
5.4 循环设计策略
- 设计循环的关键是确定需要重复执行的代码,以及编写结束循环的条件代码
程序清单 5-4 SubtractionQuizLoop.java
import java.util.Scanner;public class SubtractionQuizLoop {public static void main(String[] args) {//Number of questionsfinal int NUMBER_OF_QUESTIONS = 5;//Count the number of current answersint correctCount = 0;//Count the number of questionsint count = 0;long startTime = System.currentTimeMillis();//output string is initially emptyString output = " ";Scanner input = new Scanner(System.in);while (count <NUMBER_OF_QUESTIONS){// 1.Generate two random single-digit integersint number1 = (int) (Math.random() * 10);int number2 = (int) (Math.random() * 10);//2.If number1 < number2 ,swap number1 with number2if (number1 < number2){int temp = number1;number1 = number2;number2 = temp;}//3.Prompt the student to answer "What is number1 - number2?"System.out.print("What is " + number1 + " - " + number2 + " ? ");int answer = input.nextInt();//4.Grade the answer and display the resultif(number1 -number2 == answer){System.out.println("You are correct!");correctCount++; //Increase the correct answer count}else {System.out.println("Your answer is wrong.\n" + number1+ " - " + number2 + " should be " + (number1 - number2));}//Increase the question countcount++;output += "\n" + number1 + " - " + number2 + " = " + answer+((number1 - number2 == answer) ? " correct ": " wrong ");}long endTime = System.currentTimeMillis();long testTime = endTime - startTime;System.out.println("Correct count is " + correctCount +"\nTest time is " + testTime / 1000 + " seconds\n" + output);}}
5.5 使用用户确认或者标记值控制循环
- 使用标记值来结束输入是一种通常的做法
- 另一种控制循环的常用技术是在读取和处理一组值时指定一个特殊值。这个特殊的输入值也称为标记值(sentinel value),用以表明循环的结束。如果一个循环使用标记值来控制它的执行,它就称为标记位控制的循环(sentinel - controlled - loop)
程序清单 5-5 SentimelValue.java
import java.util.Scanner;public class SentimelValue {/** Main method*/public static void main(String[] args) {//Create a ScannerScanner input = new Scanner(System.in);//Read an initial dataSystem.out.println("Enter an integer (the input ends if it is 0) : ");int data = input.nextInt();//Keep reading data until the input is 0int sum = 0;while (data != 0){sum += data;//Read the next dataSystem.out.println("Enter an integar (the input ends if it is 0): ");data = input.nextInt();}System.out.println("The sum is " + sum);}}
- 不要比较浮点值是否相等来进行循环控制。因为浮点值都是近似值,使用它们可能导致不精确的循环次数和不准确的结果。
考虑下面计算1 + 0.9 + 0.8 + … + 0.1的代码:
double item = 1;double sum = 0;while(item != 0) {//No guarantee item will be 0sum += item;item -= 0.1;}System.out.println(sum);
变量item从1开始,每执行一次循环体就减去0.1。当item变为0时循环应该终止。但是,因为浮点数是近似的,所以不能确保item值正好为0。从表面上看,这个循环似乎没问题,但实际上它是一个无限循环。输入重定向(input redirection)
- 输出重定向(output redirection)
5.6 do - while循环
- do - while循环和while循环基本一样,不用的是它先执行循环体一次,然后判断循环继续条件。
程序清单 5-6 TestDoWhile.java
import java.util.Scanner;public class TestDoWhile {public static void main(String[] args) {int data;int sum = 0;//Create a ScannerScanner input = new Scanner(System.in);//Keep reading data until the input is 0do {//Read the next dataSystem.out.println("Enter an integer (the input ends if it is 0 ): ");data = input.nextInt();sum += data;}while (data != 0);System.out.println("The sum is " + sum);}}
- 如果循环中的语句至少需要执行一次,建议使用do - while 循环。前面程序TestDoWhile中do - while循环的情形就是如此。如果使用while循环,那么这些语句必须在循环前和循环内部都出现。
5.7 for循环
- for循环具有编写循环的简明语法
- 一般情况下,for循环使用一个变量来控制循环体的执行次数,以及什么时候能循环终止。这个变量称为控制变量(control variable)。初始操作是指初始化控制变量,每次迭代后的操作通常会对控制变量做自增或自减,而循环继续条件检验控制变量是否达到终止值。例如,下面的for循环打印Welcome to Java!100次
- 控制变量必须在循环控制结构体内或循环前说明。如果循环控制变量只在循环内使用而不在其他地方使用,那么在for循环的初始操作中声明它是一个良好的编程习惯。如果在循环控制结构体内声明变量,那么在循环外不能引用它。例如,不能在前面代码的for循环外引用变量i,因为它是在for循环内声明的。
- for循环中的初始操作可以是0个或是多个以逗号隔开的变量声明语句或赋值表达式。
- 如果省略for循环中的循环继续条件,则隐含地认为循环继续条件为true。因此,下面图a中给出的语句和图b中给出的语句一样,它们都是无限循环。但是,为了避免混淆,最好还是使用图c中的等价循环
5.8 采用哪种循环
- 可以根据哪个更加方便来使用for循环、while循环或者do - while循环
- while循环和for循环都称为前测循环(pretest loop),因为继续条件是在循环体执行之前检测的,do - while循环称为后测循环(posttest loop),因为循环条件是在循环体执行之后检测的。
- 在for子句的末尾和循环体之间多写分号是一个常见的错误,如下面的图a中所示。图a中分号表明循环过早地结束了。循环体实际上为空,如图b所示。图a和图b是等价的,都不正确。类似地,图c中的循环也是错的,图c与图d等价,都是不正确的
5.9 嵌套循环
- 一个循环可以嵌套在另外一个循环中
- 嵌套循环是由一个外层循环和一个或多个内层循环组成的。每当重复执行一次外层循环时,将再次进入内部循环并重新开始执行
程序清单 5-7 MultiplicationTable.java
public class MultiplicationTable {/*** Main method*/public static void main(String[] args) {//Display the table headingSystem.out.println(" Multiplication Table");//Display the number titleSystem.out.print(" ");//四个spacefor (int j = 1; j <= 9; j++) {System.out.print(" " + j);//三个space}System.out.println("\n----------------------");//Display table bodyfor (int i = 1; i <= 9; i++) {System.out.print(i + " | ");for (int j = 1; j <= 9; j++) {//Display the product and align properlySystem.out.printf("%4d", i * j);}System.out.println();}}}
- 需要注意的是,嵌套循环将运行较长时间。
5.10 最小化数值错误
- 在循环继续条件中使用浮点数将导致数值错误
程序清单 5-8 TestSum.java
public class TestSum {public static void main(String[] args) {//initialize sumfloat sum = 0;//Add 0.01,0.02,...,0.99,1 to sumfor (float i = 0.01f; i <= 1.0f; i = i + 0.01f) {sum +=i;}//Display resultSystem.out.println("The sum is " + sum);}}
5.11 示例学习
- 循环对于编程来说非常关键。编写循环的能力在学习Java编程中是非常重要的。
- 如果你可以使用循环编写程序,你便直到了如何编程!因此,本节提供三个运用循环来解决问题的补充示例
5.11.1 求最大公约数
程序清单 5-9 FutureTuition.java
import java.util.Scanner;public class GreatestCommonDivisor {/*** Main method*/public static void main(String[] args) {//Create a ScannerScanner input = new Scanner(System.in);//Prompt the user to enter two integersSystem.out.print("Enter first integer: ");int n1 = input.nextInt();System.out.print("Enetr second integer: ");int n2 = input.nextInt();//Initial gcd is 1int gcd = 1;//Possible gcdint k = 2;while (k <= n1 && k <= n2) {if (n1 % k == 0 && n2 % k == 0) {//Update gcdgcd = k;}k++;}System.out.println("The greatest common divisor for " + n1 + " and "+ n2 + " is " + gcd);}}
5.11.2 预测未来学费
程序清单 5-10 FutureTuition.java
public class FutureTuition {public static void main(String[] args) {double tuition = 10000;//Year 0int year = 0;while (tuition < 20000) {tuition = tuition * 1.07;year++;}System.out.println("Tuition will be doubled in " + year + " years");System.out.printf("Tuition will be $%.2f in %1d years", tuition, year);}}
5.11.3 将十进制数转换为十六进制数
程序清单 5-11 Dec2Hex.java
import java.util.Scanner;public class Dec2Hex {/*** Main method*/public static void main(String[] args) {//Create a ScannerScanner input = new Scanner(System.in);//Prompt the user to enter a decimal integerSystem.out.print("Enter a decimal number: ");int decimal = input.nextInt();//Convert decimal to hexString hex = "";while (decimal != 0) {int hexValue = decimal % 16;//Convert a decimal value to a hex digitchar hexDigit = (0 <= hexValue && hexValue <= 9) ?(char) (hexValue + '0') : (char) (hexValue - 10 + 'A');hex = hexDigit + hex;decimal = decimal / 16;}System.out.println("The hex number is " + hex);}}
5.12 关键字break 和 continue
- 关键字break 和 continue在循环中提供了额外的控制
- 关键字break 和 continue都可以在循环语句中使用,为循环提供额外的控制。在某些情况下,使用break和continue可以简化程序设计。但是,过度使用或者不正确地使用它们会使得程序难以读懂也难以调试。
程序清单 5-12 TestBreak.java
public class TestBreak {public static void main(String[] args) {int sum = 0;int number = 0;while (number < 20) {number++;sum += number;if (sum >= 100) {break;}}System.out.println("The number is " + number);System.out.println("The sum is " + sum);}}
程序清单 5-13 TestContinue.java
public class TestContinue {public static void main(String[] args) {int sum = 0;int number = 0;while (number < 20) {number++;if (number == 10 || number == 11) {continue;}sum += number;}System.out.println("The sum is " + sum);}}
- continue语句总是在一个循环内。在while和do - while循环中,continue语句之后会马上计算循环继续条件;而在for循环中,continue语句之后会立即先执行每次迭代后的动作,再计算循环继续条件
- 很多程序设计语言都有goto语句。goto语句可以随意地将控制转移到程序中的任意一条语句上,然后执行它。这使程序很容易出错。Java中的break语句和continue语句是不同于goto语句的。它们只能运行在循环中或者switch语句中。break语句跳出整个循环,而continue语句跳出循环的当前迭代
- 编程是一个富于创造性的工作。有许多不同的方式来编写代码。事实上,可以通过更加简单的代码来找到最小因子
5.13 示例学习: 判断回文
- 本节给出了一个程序,用于判断一个字符串是否回文
程序清单 5-14 Palindrome.java
import java.util.Scanner;public class Palindrome {/*** Main method*/public static void main(String[] args) {//Create a ScannerScanner input = new Scanner(System.in);//Prompt the user to enter a stringSystem.out.print("Enter a string: ");String s = input.nextLine();//The index of the first character in the stringint low = 0;//The index of the last character in the stringint high = s.length() - 1;boolean isPalindrome = true;while (low < high) {if (s.charAt(low) != s.charAt(high)) {isPalindrome = false;break;}low++;high--;}if (isPalindrome) {System.out.println(s + " is a palindrome");} else {System.out.println(s + " is not a palindrome");}}}
5.14 示例学习: 显示素数
程序清单 5-15 PrimeNumber.java
public class PrimeNumber {public static void main(String[] args) {//Number of primes to displayfinal int NUMBER_OF_PRIMES = 50;//Display 10 per linefinal int NuMBER_OF_PRIMES_PER_LINE = 10;// Count the number of prime numbersint count = 0;// A number to be tested for primenessint number = 2;System.out.println("The first 50 prime number are \n");//Repeated find prime numberswhile (count < NUMBER_OF_PRIMES) {//Assume the number is prime//Is the current number prime?boolean isPrime = true;//Test whether number is primefor (int divisor = 2; divisor <= number / 2; divisor++) {// If true ,number is not primeif (number % divisor == 0) {//Set isPrime to falseisPrime = false;//Exit the for loopbreak;}}//Display the prime number and increase the countif (isPrime) {count++; //Increase the count}if (count % NuMBER_OF_PRIMES_PER_LINE == 0) {//Display the number and advance to the new lineSystem.out.println(number);} else {System.out.print(number + " ");}}//Check if the next number is primenumber++;}}
