第3 章〓控制语句与算法



本章练习



Java语言与所有的程序设计语言一样,使用选择语句与循环语句控制程序的执行流程。本章主要介绍选择语句、循环语句的语法格式,以及这些语句的用法,并结合具体的例子介绍如何使用这些语句进行算法设计。



观看视频

3.1Java程序的执行流程
3.1.1算法的执行

Java语言是一种典型的面向对象程序设计语言,整个程序都是由对象组成的,对象中包含属性与方法两部分。在方法部分编程时,只有输入、计算与输出语句是无法实现复杂的算法功能的,例如,对于求和运算f(n)=∑ni=11i
,求出f(n)>=10的最小n。针对这个问题,可以设计如下的算法。

(1) 初始化变量: “f=0,i=0;”。

(2) 如果f<10,则转(3),否则转(6)。

(3) i=i+1。

(4) f=f+1/i。

(5) 转(2)。

(6) n=i,返回n。

从这个简单的算法可以看出,算法的执行是从上到下逐条顺序执行的,在这个顺序执行过程中,需要进行选择判断,并根据选择判断结果控制执行流程(语句)。语句(2)为判断语句,语句(3)~(5)是一个循环累加过程。

Java语言提供了实现上述基本控制结构的语句,有了这些流程控制语句,就可以为任何复杂算法编写程序。

3.1.2语句块与块作用域

在Java程序中,语句块的概念很重要。语句块又称为复合语句,是指由一对{}括起来的0到多条语句。一个语句块中的语句可以看作一个整体,它们要么都执行,要么都不执行,一个语句块可以嵌套在另一个语句块内。

语句块确定了块内变量的作用域。下面的代码是在main()方法中嵌套一个语句块,在该语句块中定义了一个块内变量i。



public static void main(String[] args) {

float f=0;

…

{ int i=0;

…

}//变量i只在本语句块内可见

…

 }





在上面的代码中,变量f在整个main()方法内可见,即可以访问,而变量i只在嵌套的语句块内可见,在语句块外是不存在的。

Java不允许在嵌套的两个块中声明同名的变量,例如,下面的代码会报编译错误。



public static void main(String[] args) {

float f=0;

…

{ int i=0;

float f;//编译出错,f不能重复定义

…

}

…

}





3.2选择语句

选择语句又称为条件语句、分支语句。该语句提供了基于条件的不同,选择不同执行路径的能力。Java语言的选择语句有两种,分别是if语句与switch语句。

3.2.1if语句

if语句有三种具体格式,分别是简单if语句、ifelse语句和复合if语句。图31给出了这三种if语句的流程图。








图31if语句的流程图


1. 简单if语句

简单if语句的格式为



if  (<逻辑表达式>)

<语句块>





其中,<逻辑表达式>的取值只能是true或false,而且必须用圆括号括起来。该if语句的执行流程是先计算<逻辑表达式>的值,如果该值为true,则执行<语句块>中的语句,否则不执行<语句块>。如果<语句块>只有一条语句,{}可以省略。例如: 



if (a>b)

System.out.println("a 大于 b");





与



if (a>b)

{System.out.println("a 大于 b");}





是等价的。

2. ifelse语句

ifelse语句的格式为



if  (<逻辑表达式>) 

<语句块1>

else 

<语句块2>





该语句执行时,首先计算<逻辑表达式>的值,如果该值为true,则执行<语句块1>,否则执行<语句块2>。ifelse语句又称为二择一选择语句。这里<语句块1>与<语句块2>是指用{}括起来的0到多条语句,如果只有一条语句,{}可以省略。

3. 复合if语句

复合if语句又称为多择一if语句,其格式为



if  (<逻辑表达式1>) 

<语句块1>

else if  (<逻辑表达式2>)

<语句块2> 

…

else

<语句块n>





该语句执行时,首先计算<逻辑表达式1>的值,如果该值为true,则执行<语句块1>,否则计算<逻辑表达式2>的值,如果该值为true,则执行<语句块2>,以此类推。如果所有的逻辑表达式的值都为假,则执行<语句块n>。这种复合的if语句实际上是从多个条件中从上到下逐个判断,择一执行。

【例31】输入三个数,按由小到大的顺序输出。



import java.util.Scanner;

public class Example3_1 {

public static void main(String [] args){

float a,b,c,t;

Scanner sc=new Scanner(System.in);

a=sc.nextFloat();

b=sc.nextFloat();

c=sc.nextFloat();

if (a>b)

{t=a;

a=b;

b=t;

}

if (c<a)

System.out.println("三个数由小到大:"+c+"   "+a+"   "+b );

else if (c>=b)

System.out.println("三个数由小到大:"+a+"   "+b+"   "+c );

else

System.out.println("三个数由小到大:"+a+"   "+c+"   "+b );

}

}





在Example3_1中,用Scanner对象sc从键盘上读取三个数据,输入的三个数分别存到变量a、b、c中。在输入数据时,三个数据用空格分隔,最后按Enter键。下面是该程序运行时的数据输入与结果输出。



231

三个数由小到大: 1.02.03.0





程序中,if(a>b){…}是一个简单的if语句,该语句实现把两个数中大的放到a,小的放到b。第二个if语句是一个多择一的复合if语句。



观看视频

3.2.2switch语句

在处理多个选择时,使用多择一的复合if语句有时显得有些笨拙。为此,许多编程语言提供了switch语句,该语句的语法结构如下。



switch (<switch表达式>) { 

case value1:<语句块1>; 

break;

case value2:<语句块2>; 

break; 

…

case valueN:<语句块N>; 

break; 

default:<其他语句块>; 

}





图32给出了switch语句的执行流程。从该流程中可以看出,在switch语句执行时,首先计算<switch表达式>的值 ,然后判断该值是否等于value1,如果相等,则执行<语句块1>与break; 否则判断该值是否等于value2,如果相等,则执行<语句块2>与break,以此类推。如果该值与所有的value值都不等,则执行default后的<其他语句块>。



图32switch语句的执行流程


这里需要特别说明的是在每个case语句末尾的break语句,break语句的功能是跳出该switch语句,执行流程跳到switch之后的语句。如果在case之后没有break语句,程序会继续执行下一条case语句后的<语句块>,直到遇到break或到达switch的末尾。

switch语句有如下规则。

(1) switch语句可以拥有多条case语句。每个case后面跟一个要比较的值和冒号。

(2) switch语句中的switch 表达式的类型只能为byte、short、int、char与字符串,且case语句中值的数据类型必须与之相同。

(3) 当switch表达式的值与case语句的值相等时,case语句之后的语句开始执行,当遇到break语句时,程序跳转到switch语句后面的语句执行。

(4) case语句不必须包含break语句,但如果没有break语句,程序会继续执行下一条case语句后的语句块。

(5) switch语句的最后可以包含一个default分支,其含义是在没有case语句的值和switch 表达式值相等时,执行其后的<其他语句块>。

【例32】编写一个程序,把百分制的成绩转换成五级分制。

在学生成绩登记中,百分制向五级分制的转换规则是90~100分为优、80~89分为良、70~79分为中、60~69分为及格、60分以下为不及格。下面的程序就是按照这个规则实现转换的。



import java.util.Scanner;

public class Example3_2 {

public static String ptoword(float grad) {/* 输入分数转换为文字 */

String result = null; 

switch  ((int)grad/10) {

case 10:

case 9:

result = "优";

break;

case 8:

result = "良";

break; 

case 7:

result = "中";

break;

case 6:

result = "及格";

break;

default:

result = "不及格";

}

return result;


}

public static void main(String[] args)

{float point=0;

Scanner scan=new Scanner(System.in);

while (true)

{

System.out.println("Please input score 0--100:");

point=scan.nextFloat();

if (point>=0 & point <=100)

System.out.println("The point is: "+ptoword(point));

else 

System.out.println("The score should be 0--100");

}

}

}







观看视频

3.3循环语句

当程序中出现了大量的重复且有规律的代码段时,可以用循环语句控制重复代码段的执行。例如,如果需要输出100次“欢迎,欢迎,热烈欢迎” ,不用循环语句会写成下面的形式。



System.out.println("1. 欢迎,欢迎,热烈欢迎!"); 

System.out.println("2. 欢迎,欢迎,热烈欢迎!"); 

…

System.out.println("100. 欢迎,欢迎,热烈欢迎!");





显然这种写法显得太笨拙了,如果使用循环语句,代码会变得非常简洁。上面的代码用循环语句实现如下:



for (int i=1;i<=100;i++)

System.out.println(i+". 欢迎,欢迎,热烈欢迎!");





Java语言的循环语句有三种,分别是for循环、while循环与dowhile循环。三种循环语句各有所适应的循环场景,编程人员可以根据实际问题选择合适的循环语句。

3.3.1while语句

while语句是一种常用的循环语句,其语法格式如下。



while (<逻辑表达式>) {

<循环体>;

}







图33while语句的执行流程

其中,<逻辑表达式>是循环控制条件,其值是布尔值true或false。在循环开始执行时,先计算一次<逻辑表达式>的值,若条件为false,则while语句结束; 否则执行循环体中的语句并返回到while的开始,继续计算<逻辑表达式>并根据该值确定是否继续循环。图33给出了while语句的执行流程。


【例33】f(n)=∑ni=11i
,编程求出f(n)≥10的最小n。



public class SmallestN {

public static void main(String[] args) {

double f=0;

long n,i=0;

while(f<10){

i=i+1;

f=f+1.0/i;

}

n=i;

System.out.println("Smallest n="+n);

System.out.println("f(n)="+f);

}

}





该程序的运行结果如下。



Smallesst n=12367

f(n)=10.000043008275778





3.3.2dowhile语句

dowhile语句的语法格式如下:



do

<循环体>

while(<逻辑表达式>);







图34dowhile语句的执行流程

其中,<逻辑表达式>是循环控制条件,其值是布尔类型。dowhile语句的执行与while语句稍有不同。在while语句中,如果一开始的<逻辑表达式>为false,则循环体一次都不执行; 而dowhile语句的执行流程是先执行循环体,然后再计算<逻辑表达式>,如果该值为true,则继续循环,直到该值为false。因而,dowhile语句至少执行一次<循环体>中的语句。这里的<循环体>通常是一个语句块,dowhile语句的执行流程见图34。


【例34】从键盘上输入若干数,求这组数据的平方和s,当平方和s>108或者输入的数为0时,输出数据个数与平方和s,程序结束。



import java.util.Scanner;

public class Example3_4 {

public static void main(String[] args) {

double s=0,x;



int i=0;

Scanner sc=new Scanner(System.in);

do {

x=sc.nextDouble();

i=i+1;

s=s+Math.pow(x,2);

} while (x!=0 && s<=1.0e+8);    

System.out.println("输入数据的平方和="+s);

System.out.println("输入数据个数="+i);

}

}





在以上程序中,用Scanner对象sc从键盘上读取double类型的值并赋值给x,然后计算x的平方并累加到s中,接下来计算逻辑表达式(x!=0 && s<=1.0e+8)的值,如果该值为true则继续循环,否则,循环结束,执行后面的输出语句。

需要注意的是,判断输入的double类型x是否为0,应当用Math.abs(x)小于一个足够小的数(这里用1.0e-10,即10-10),最好不用x!=0,这是因为在编程语言中,由有限的二进制位数表示浮点数会存在误差,为了代码安全,当比较两个浮点数是否相等时,通常不用“==”判断,而是用它们的差足够小来判断。

Math.pow(x,2)、Math.abs(x)是Math类中的静态方法,分别是计算x2与|x|。

3.3.3for语句

for语句是一种常用的循环语句,语法形式如下: 



for(<初始化表达式>;  <循环条件表达式>;  <循环后操作表达式>)

<循环体>







观看视频

for循环把循环的初始化操作、循环条件、每次循环后的操作都放在for后面的圆括号中,表达式之间用英文分号分隔。这里的<循环体>为迭代执行的语句块,是每次循环执行的语句序列。for语句的执行流程如图35所示,具体的执行过程如下。



图35for语句能够执行流程


第一步,先执行初始化表达式操作,对循环变量赋初值。在整个循环过程中,对循环变量赋初值操作只执行一次。

第二步,计算<循环条件表达式>的值。若该值为true,表示循环条件成立,则执行<语句块>,然后执行<循环后操作表达式>,再回到本步的开头进行下一次循环。若该值为false,则循环执行结束,执行流程跳到for循环的后继语句。

for语句中的三个表达式可以都为空,即for(;;),表示是一个无限循环(又称死循环)。在这种情况下,循环体中需要根据执行情况,用break语句控制跳出循环,否则,程序会进入死循环。

【例35】编程计算1+11!+12!+…+110!。



public class Example3_5 {

public static void main(String[] args) {

double s=1;

long f=1;

for (int i=1;i<=10;i++)

{f=f*i;

s=s+1.0/f;

}








System.out.println("Eular number="+s);

}

}





在程序中,用f存放i!的值,即当i=1时,f中是1!; 在下次循环时,当i变成2时,执行f=f*i,把2!赋值给f,以此类推,当i循环到10时,f中存放的是10!。

3.4跳转语句

在循环语句中使用break与continue语句可以提供对循环过程的额外控制。在某些情况下,使用break与continue语句可以简化编程。尽管Java提供了这种跳转语句,但由于使用它们可能会使程序难以阅读和调试,在编程时,尽量不要使用它们。

3.4.1break语句

在循环执行过程中,如果想在特定条件下退出循环的执行,可以调用break语句。下面的程序给出了在循环中使用break语句的效果。



public class BreakDemo {

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);

}

}





将整数从1到20相加,直到和大于或等于100时,循环结束。如果没有if语句,程序将计算1~20的数字之和。在使用if语句时,当和sum大于或等于100时,break语句控制执行流程跳出循环。

3.4.2continue语句

continue语句可以用在任何循环控制结构中,其功能是跳过循环体中continue后剩余的语句而强行执行下一次循环,即终止当前迭代,进入下一次循环。它用在for循环体与while循环体中,执行过程略有不同。

(1) 在for循环中,continue语句将导致控制流程立即跳到<循环后操作表达式>,更新循环变量,然后进行下一次循环条件判断。

(2) 在while循环或者dowhile 循环中,continue语句控制流程立即跳到<逻辑表达式>计算,并根据该值决定是否继续循环。

下面的程序对1~99中的每个数进行判断,如果这个数是7的倍数或者个位为7,则执行continue语句,跳转到while<逻辑表达式>计算,并根据该<逻辑表达式>的值决定是否继续循环。



public class ContinueDemo {

public static void main(String[] args) {

int sum = 0;

int number = 0;

while (number < 100) {

number++;

if (number % 7 == 0 || number % 10 == 7)

continue;

sum += number;

}

System.out.println("The sum is " + sum);

}

}





该程序最终的运行输出是“The sum is 3879”,该值是1~99中不是7的倍数并且末位不是7的所有数字之和。

3.5控制语句编程举例

本节通过例子介绍常用的算法设计。

【例36】计算一元二次方程ax2+bx+c=0的根,系数a、b、c从键盘输入。

该程序需要考虑以下几种情况。

(1) a、b都为0,程序应输出“不是方程”信息。

(2) a为0并且b不为0,程序是一元一次方程。

(3) a、b都不为0,这时要判断b2-4ac的值是否大于或等于0。若b2-4ac≥0,则输出两个实数,否则,显示方程“没有实数根”。

程序编写如下:



import java.util.*;

public class Example3_6  {

public static void main(String[] args) {

double a,b,c,x1,x2;

Scanner scan=new Scanner(System.in);

System.out.println("input a,b,c: ");

a=scan.nextDouble();

b=scan.nextDouble();


c=scan.nextDouble();

if (a==0)

if (b==0)

System.out.println(" a,b 都为0,不是方程");

else

System.out.println(" a为0,只有一个根 x="+(-c/b));

else

if (b*b-4*a*c>=0)








{ x1=(-b+Math.sqrt(b*b-4*a*c))/2/a;

x2=(-b-Math.sqrt(b*b-4*a*c))/2/a;

System.out.println("x1="+x1+"   x2="+x2);

}

else

System.out.println("没有实数根");

}

}





在该程序中,在if(a==0)…else…之后各嵌入了一条ifelse语句。在这种ifelse的嵌套中,ifelse的配对方法是else总是和上方还没有配对的if配对。

【例37】求两个正整数的最大公约数。

对于从键盘上输入的两个整数n1与n2,需要先判断是不是都是正整数,如果不是,则显示"n1 和 n2 必须是大于0的整数"。如果两个数都是正整数,程序中先把两个数的较小者赋值给变量n。由于两个数的最大公约不会大于变量n的值,因而可以构建循环,判断变量n,变量n-1,…,1能不能整除n1和n2。编写程序如下:



import java.util.Scanner;

public class Example3_7 {

public static void main(String[] args) {

Scanner input = new Scanner(System.in);

System.out.print("输入第一个整数: ");

int n1 = input.nextInt();

System.out.print("输入第二个整数: ");

int n2 = input.nextInt();

int gcd = 1;

int n;

if (n1 > 0 && n2 > 0) {

if (n1 >= n2)

n = n2;

else

n = n1;

while (n>=1) {

if (n1 % n == 0 && n2 % n == 0)

{gcd = n;

break;

}

n--;

}

System.out.println( n1 + " 和 " + n2 + " 最大公约数是: " + gcd);

} else


System.out.println("n1 和 n2 必须是大于0的整数");

}

}





【例38】在屏幕上输出九九乘法口诀表。

要输出一个九九乘法口诀表,需要二重循环。程序代码与输出如下:



import java.util.Scanner;

public class Example3_8 {








public static void main(String[] args) {


int i,j,n;

Scanner scan=new Scanner(System.in);

System.out.print("输入 n");

n=scan.nextInt();

System.out.println("九九乘法口诀表");

i=1;

System.out.print("      ");

while (i<=n)

{System.out.printf("%6d",i);

i++;

}

System.out.println();

i=1;

while (i<=n)

{System.out.print("-------");

i++;

}

System.out.println();

i=1;

while (i<=n)

{System.out.printf("%2d",i);

System.out.print("|   ");

j=1;

while (j<=n)

{

System.out.printf("%6d",i*j);// 使用格式化输出

j++;

}

System.out.println();

i++;

}

}

}





为了使输出的九九乘法口诀表每列对得整齐,需要让每列数据占相同的宽度,为此程序中使用printf("%6d",x)格式化输出整数x,其含义是以6为宽度、右对齐的格式输出整数x。程序的运行结果如下:



输入n: 10

九九乘法口诀表

12345678910





1|12345678910

2|2468101214161820

3|36912151821242730

4|481216202428323640

5|5101520253035404550

6|6121824303642485460

7|7142128354249566370

8|8162432404856647280

9|9182736455463728190

10|102030405070708090100





【例39】求所有的水仙花数。

水仙花数是指一个3位整数,它的每个位上的数字的3次幂之和等于它本身,例如: 13+53+33=153。本程序采用穷举法,逐个测试三位整数(100~999)是不是水仙花数。程序代码如下:



public class Example3_9 {

public static void main(String[] args) {

System.out.print("水仙花数有:");

int i,j,k;

int num1,num2;

for (i=1;i<=9;i++)

for (j=0;j<=9;j++)

for (k=0;k<=9;k++){

num1=(int)(Math.pow(i,3)+Math.pow(j,3)+Math.pow(k,3));

num2=i*100+j*10+k;

if (num1==num2)

System.out.printf("%7d",num2);

}

}

}





运行该程序,输出结果如下:



水仙花数有: 153370371407





【例310】输出斐波那契数列的前20项。

斐波那契数列指的是这样一个数列: 1,1,2,3,5,8,13,21,34,55,89,…,这个数列从第3项开始,每一项都等于前两项之和。基于这一描述,可以用循环迭代的方法,每次循环中,用序列中前面两个值计算当前值,输出该序列,程序代码如下:



public class Example3_10 {

public static void main(String[] args) {

int a=1;

int b=1;

int t;



System.out.printf("%6d%6d",a,b);

for (int i=2;i<=20;i++){

t=a+b;

System.out.printf("%6d",t);

a=b;

b=t;

}

}

}





对于斐波那契数列,还可以采用如下的递归形式进行定义。

f(n)=1,n=1或n=2
f(n-1)+f(n-2),n>2

Java允许递归调用,即一个方法直接或间接地调用自己。采用递归编程,程序代码如下。



public class FibonacciRecursion {

public static int fib(int n){

if (n<=0)

return -1;

else if (n<=2)

return 1;

else

return fib(n-1)+fib(n-2);

}

public static void main(String [] args){

int i;

System.out.println("斐波那契数列:");

for (i=1;i<=20;i++)

System.out.printf("%6d",fib(i));



}

}





在编程时,虽然递归算法使程序看来非常简洁,但是递归程序需要频繁地进行方法调用,运行效率比较低,计算时间比较长。

习题

1. 阅读以下Java语言程序的片段,写出程序的输出结果。



int t = 198;

do{

System.out.println(t);

t = t / 2;

} while(t > 0);

System.out.println("t="+t);





2. 阅读以下Java语言程序的片段,写出程序的输出结果。



for (int i = 1; i < 4; i++) { 

for (int j = 1; j < 4; j++) { 

if (i * j > 2) 

continue; 

System.out.println(i * j); 

} 

System.out.println(i); 

}





3. 把下面的代码转换成switch语句。



if (a == 1) 

x += 5; 

else if (a == 2) 

x += 10; 

else if (a == 3) 

x += 16; 








else if (a == 4) 

x += 34;

else

 x += 100;





4. while循环和dowhile循环的区别是什么?将以下循环转换为dowhile循环执行。



int sum = 0; 

int number = input.nextInt(); 

while (number != 0) { 

sum += number; 

number = input.nextInt(); 

}





5. 在循环中,break与continue的作用是什么?下面的两程序段哪个存在问题?为什么?



int x=200;int x=200;

while(true){while(true){

if(x<9)if(x<0)

break;continue;

x=x-8;x=x-8;

}}

System.out.println("x is"+x);System.out.println("x is"+x);





6. Math.random()可以产生0~1随机的小数,通过运算可以把该数转换成0~100的随机整数。先编程产生0~100的随机整数x,然后让用户输入对该数的猜测值guess。如果guess等于x,则显示“猜测正确”,否则显示guess值是大了还是小了,提示用户继续猜测,直到猜测正确为止。

7. 编写一个程序,从键盘读取一个整数,编写程序显示其所有最小的因子。例如,如果输入整数为48,则输出如下: 2、2、2、2、3。

8. 从键盘输入一个整数,判断并显示该数是不是素数。

9. 回文素数是指一个整数n从左向右和从右向左读其结果值相同且是素数。编程求出1000~100000的回文素数。

10. 输入三角形的三条边a、b、c,判断并输出能否构成三角形(任意两条边的和大于第三条边),如果能构成三角形,输出是什么类型的三角形(等边、等腰、直角)。

11. 编写一个程序,提示用户输入一个1~20的数,并显示一个金字塔,示例运行结果如下所示。



"C:\\Program Files\\Java\\jdk1.8.0_281\\bin\\java.exe"…

Please input n:6

1

212

32123

4321234

543212345

65432123456






12. 编写一个程序,提示用户输入'A'~'Z'中的一个字符,并显示一个金字塔。例如,输入H,则显示由A到H形成的金字塔,示例如下。



"C:\\Program Files\\Java\\jdk1.8.0_281\\bin\\java.exe"…

请输入字符: H

A

BAB

CBABC

DCBABCD

EDCBABCDE

FEDCBABCDEF

GFEDCBABCDEFG

HGFEDCBABCDEFGH






13. 给出递归计算n!的方法factorial(int n),并编写主程序调用该递归方法。

14. 用Java语言编写程序,计算e=1+1/1!+1/2!+…+1/n!。要求e值精确到小数点后第6位。试比较n!计算调用递归方法与不用递归方法的运行时间差异。

15. 完全数又称完美数,即如果一个正整数等于它的所有正因数的和,则它被称为完全数。例如,6是第一个完全数,因为6=3+2+1,下一个完全数是28(28=14+7+4+2+1)。编写一个程序来找到1~10000所有的完全数。

16. 在数学上可以用下面的公式计算π。

π=41-13+15-17+…+12i-1-12i+1

编程显示i=100、1000、10000、100000时的π值。

17. 编程统计全班的90~100分、80~89分、70~79分、60~69分、不及格的人数。学生成绩由键盘输入,输入-1表示结束。