第3章 流程控制语句
流程就是指程序执行的顺序,流程控制就是指通过控制程序执行的顺序实现要求的功能。流程控制部分是程序中语法和逻辑的结合,也是程序中最灵活的部分,是判断一个程序员的能力的主要方面。众所周知,算法是程序逻辑的核心,而算法的绝大部分代码都是流程控制实现的。本章介绍Java中控制程序流程的语句,包括条件语句、循环语句、转移(分支)语句等。
实例9 打印任一年日历
在程序中,需要根据某个条件重复地执行某项任务若干次,直到满足或不满足某条件为止,这就是程序流程的控制。流程控制可以使程序员用很少的语句,让计算机重复完成大量的计算,而且使程序的结构在逻辑上更清晰易读。经常用到的控制流程的语句包括for循环、while循环、do-while循环、多路分支switch-case、if-else等。下面将综合运用这些流程语句编写任一年的日历。
技术要点
本实例主要说明如何使用控制流程语句及它们的使用规范。技术要点如下:
• for语句使用最为灵活,不仅可以用于循环次数已经确定的情况,也可以用于循环次数不确定而只给出循环条件结束的情况,可以完全替代while语句。一般形式为:for(循环变量赋初值;循环条件;循环变量改变值)。
• while的一般形式为:
while(condition){语句}
while的执行流程:求出表达式condition的值,若值为真,则执行循环体,直到表达式condition的值为假,就退出循环。while是先判断后执行。
• do-while的一般形式为:
do{语句}while(condition);
无论表达式的值是真是假,其循环体总先被执行一次,然后再判断循环结束条件。
• switch-case语句用来专门处理“多中择其一”的情况语句,故又称为多路选择语句或开关语句。在这种情况下,使用switch语句写出的程序往往比使用if-else语句写的程序更简洁、清晰,且不易出错。
• if语句用来判定所给定的条件是否满足,根据判定的结果(真或假)决定执行哪块语句。
实现步骤
(1)新建一个类名为TextControl.java。
(2)代码如下所示:
package com.zf.s3 //创建一个包 import java.io.*; public class TextControl{ //操作打印任一年日历的类 static int year, monthDay, weekDay; //定义静态变量,以便其他类调用 public static boolean isLeapYear(int y) { //判断是否是闰年 return ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)); } public static int firstDay(int y) { //计算该年第一天是星期几 long n = y * 365; for (int i = 1; i < y; i++) if (isLeapYear(i)) //判断是否是闰年 n += 1; return (int) n % 7; } public static void printWeek(){ //打印表头 System.out.println("==========================="); System.out.println("日 一 二 三 四 五 六"); } public static int getMonthDay(int m){ //获取每个月的天数 switch (m) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: return 31; case 4: case 6: case 9: case 11: return 30; case 2: if (isLeapYear(year)) //判断是否是闰年 return 29; else return 28; default: return 0; } } public static void printMonth(){ //分别按不同条件逐月打印 for (int m = 1; m <= 12; m++) //循环月份 { System.out.println(m + "月"); printWeek(); for (int j = 1; j <= weekDay; j++){//按每个月第一天是星期几打印相应的空格 System.out.print(" "); } int monthDay = getMonthDay(m); //获取每个月的天数 for (int d = 1; d <= monthDay; d++) { if (d < 10) //以下4行对输出格式化 System.out.print(d + " "); else System.out.print(d + " "); weekDay = (weekDay + 1) % 7; //每打印一天后,反映第二天是星期几 if (weekDay == 0) //如果第二天是星期天,便换行 System.out.println(); } System.out.println('\n'); } } public static void main(String[] args)throws IOException{//Java程序的主入口处 System.out.print("请输入一个年份:"); InputStreamReader ir; //以下接收从控制台的输入 BufferedReader in; ir = new InputStreamReader(System.in); in = new BufferedReader(ir); String s = in.readLine(); year = Integer.parseInt(s); weekDay = firstDay(year); //计算该年第一天是星期几 System.out.println("\n " + year + "年 "); printMonth(); } }
(3)运行结果如下所示:
请输入一个年份:2003 2003年 1月 =========================== 日 一 二 三 四 五 六 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
2月 =========================== 日 一 二 三 四 五 六 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
3月 =========================== 日 一 二 三 四 五 六 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ⋯
源程序解读
(1)isLeapYear()方法根据传入的年份被4整除但不能被100整除或能被400整除来判断是否是闰年。
(2)firstDay()方法判断传入的年份是否是闰年,如果是闰年则天数要加1,再取得除7的余数,余数即为星期几的表示。
(3)getMonthDay()方法根据大月小月以及2月份的月份天数的不同,运用switch-case分路分支的流程判断。注意:switch中的字符是int、short、byte、char,不能用long、String。如果case的值为空,没有break语句,则循环一直往下寻找直到遇到return;在没有符合匹配的字符串时,则进入default语句。
(4)在程序的main()方法中,运用控制台输入数值,具有很好的灵活性和可操作性。将所输入的文本从流中InputStream(字节流)传给InputStreamReader(字符流)再放到BufferStream(缓冲流)。这样有助于读完数据释放已分配的内存。
实例10 控制台输出几何图形
几何图形基本上包括长方形、三角形、菱形、梯形等,但如何通过程序输出指定的图形?本节实例将讲解如何输出直角三角形、菱形以及由规律数字组成的直角三角形。
技术要点
本实例主要运用了for的双重循环实现图形的显示。技术要点如下:
• 运用for双重循环:外循环可以控制行数,内循环在行的基础上控制每列的数目。
• 输出图形的多样性在于运用数学逻辑控制每行每列的输出,这也是本实例锻炼开发人员的逻辑思维的目的所在。
实现步骤
(1)新建一个类名为TextPrintPicture.java。
(2)代码如下所示:
package com.zf.s3; //创建一个包 public class TextPrintPicture { //操作控制台输出图形的类 public static void printRightangle(int row){ //输出直角图形 for(int i=1;i<row;i++){ //外层循环row次,输出row行 for(int j=1;j<=i;j++){ //控制本次输出的"*"数目,这个数目由i决定 System.out.print("*"); } System.out.println(); //每输完一行就要换行 } } public static void printLozenge(int row){ //输出菱形图形 if(row>=1){ //判断传入的行数 int i,j; for(i=1; i<=row; i++){ //先输出上面的正三角形 for(j=1; j<=row-i; j++) //控制本次输出的空格数,注意循环控制表达式 System.out.print(" "); for(j=1; j<=2*i-1; j++) //控制本次输出的"*"数目,注意循环控制表达式 System.out.print("*"); System.out.println(); //每输完一行就要换行 } for(i=1;i<=row;i++){ //输出下面的正三角形 for(j=1;j<=i;j++) System.out.print(" "); //打印左边的空格 for(j=1;j<=2*(row-i)-1;j++) //控制本次输出的"*"数目,注意循环控制表达式 System.out.print("*"); System.out.println(); //每输完一行就要换行 } } } public static void printNumberRightangle(int row){ //输出数字直角图形 for(int i=1;i<=row;i++){ //外层循环row次,输出row行 for(int x=1;x<i;x++) //数字由小到大排列显示 System.out.print(x); for(int j=i;j!=0;j--) //数字由大到小排列显示 System.out.print(j); System.out.println(); //每输完一行就要换行 } } public static void main(String []args){ //Java程序主入口方法 System.out.println("1.输出直角图形"); printRightangle(5); //输出行数为5的直角图形 System.out.println("2.输出菱形图形"); printLozenge(5); //输出行数为2*5-1的菱形 System.out.println("3.输出数字直角图形"); printNumberRightangle(8); //输出行数为8的数字直角图形 } }
(3)运行结果如下所示:
1.输出直角图形 * ** *** **** 2.输出菱形图形 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 3.输出数字直角图形 1 121 12321 1234321 123454321
源程序解读
(1)输出的第一个直角三角形,每行输出的“*”都在依次增加,很容易想到用双层循环来实现:外层循环控制输出的行数,即为传入的参数值;内层循环控制本行输出的“*”数目,这个数目恰好是本行的行号;每一行输出完毕后,需要输出一个换行符。
(2)输出的第二个菱形,可以把它分成两个正三角形:上面一个的行数是与传入参数值相等的正三角形,下面一个的行数是(传入参数值-1)的倒三角形。对于上面的正三角形,若以i代表行号,那么每行“*”的数目等于2i-1。对于下面的倒三角形,行数是上三角形行数-1,这样“*”的数目等于2(row-i)-1,其中row就是参数的值。对于空格:上三角形输出的空格越来越少,下倒三角形输出的空格越来越多,也是运用双重循环进行逻辑控制。
(3)输出的数字直角三角形,也是把它分为两部分完成。具体编写与输出菱形类似。
实例11 杨辉三角
杨辉三角是一个由数字排列的三角形数表。本节介绍如何实现控制台输出杨辉三角形。
技术要点
实现杨辉三角技术要点如下:
杨辉三角最本质的特征是:除两侧元素均为1以外,其余每个位置上的元素值为其正上方元素与左上角元素之和,用数组来描述则为a[i][j]=a[i-1][j-1]+a[i-1][j]。
实现步骤
(1)新建一个类名为TextTriangle.java。
(2)代码如下所示:
package com.zf.s3; //创建一个包 public class TextTriangle { //操作杨辉三角的类 public static void yanghui(int a[][], int ROW) { //输出杨辉三角 for (int i = 0; i <= ROW; i++){ //循环行数 for (int j = 0; j <= a[i].length - 1; j++) { //在行基础上循环列数 if (i == 0 || j == 0 || j == a[i].length - 1) a[i][j] = 1; //将两侧元素设为1 else //元素值为其正上方元素与左上角元素之和 a[i][j] = a[i - 1][j - 1] + a[i - 1][j]; } } for (int i = 0; i <= ROW; i++) { //循环行数 for (int j = 0; j <= a[i].length - 1; j++) //在行基础上循环列数 System.out.print(a[i][j] + " "); //输出 System.out.println(); //换行 } } public static void main(String args[]) { //Java程序主入口处 final int ROW = 5; //设置行数 int a[][] = new int[ROW + 1][]; //声明二维数组,行数为6 for (int i = 0; i <= ROW; i++) { //循环初始化数组 a[i] = new int[i + 1]; } yanghui(a, ROW); //调用方法显示杨辉三角 } }
(3)运行结果如下所示:
1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 1 0 1 0 5 1
源程序解读
在main()方法中声明二维数组并运用循环对数组赋值。调用yanghui()方法,传入数组和行数作为参数。在yanghui()方法中,运用循环将要输出的三角的两侧元素值设为1,再运用杨辉三角的性质:每个位置上的元素值为其正上方元素与左上角元素之和。将元素进行运算赋值。最后将这个二维数组元素循环显示出来,即为杨辉三角。
实例12 拜访过程(break和continue)
本实例通过模拟拜访的过程,讲解break语句与continue语句,并运用for循环与continue循环。通过两种语句的跳出循环进行比较联系和区别。
技术要点
运用break和continue跳出循环的技术要点如下:
• break语句主要用于跳出循环语句。continue通常与if语句联用,在满足条件时结束当次循环。continue可以方便地编写除把不满足要求的元素去除的逻辑,可以称为“反逻辑”。
• break与continue的区别在于:break是直接跳出循环,而continue只是中断当次循环,如果后面的条件满足要求,还会继续执行。
实现步骤
(1)新建一个类名为TextBreakAndCon.java。
(2)代码如下所示:
package com.zf.s3; //创建一个包 public class TextBreakAndCon { //操作break与continue语句 private static void showBreak(){ //测试break语句 label: //声明标签,后面要加":" for(int i=1,j=0;i<5;i++){ //定义访问次数计数器i和访问页面计数器j System.out.println("第"+i+"次拜访我"); while(j<5){ j++; System.out.println("Hello,打开了第"+j+"扇门"); if(j>2) break label; } } } private static void showContinue(){ //测试continue语句 for(int i=1;i<5;i++){ //循环测试continue if(i==2) continue; System.out.println("欢迎第"+i+"次拜访我"); } } public static void main(String []args){ //Java程序主入口处 System.out.println("1.测试break语句"); showBreak(); //调用方法 System.out.println("2.测试continue语句"); showContinue(); //调用方法 } }
(3)运行结果如下所示:
1.测试break语句 第1次拜访我 Hello,打开了第1扇门 Hello,打开了第2扇门 Hello,打开了第3扇门 2.测试continue语句 欢迎第1次拜访我 欢迎第3次拜访我 欢迎第4次拜访我
源程序解读
(1)showBreak()方法中定义标签,运用嵌套的for循环和while循环。当打开的门数大于2时,执行break标签语句,跳转到标签语句块的末尾,结束此循环,所以只输出一次拜访和打开3扇门。
(2)showContinue()方法中运用for循环,当拜访次数为2时,执行continue语句,跳出当前循环。再判断后面的条件是否合适,以决定是否继续进行循环。
常见问题 for循环初始化问题
将声明的数组或集合进行初始化,通常有两种方法:用关键字new,在声明数组的时候进行初始化;二是在循环中对数组或集合进行初始化。
技术要点
• 初始化格式如int[]a=new int{1,2,3,4,5};或用for循环依次进行初始化。
• 对基本数据类型可以进行匿名数组初始化。
实现步骤
(1)新建一个类名为QuestionOne.java。
(2)代码如下所示:
package com.zf.s3; //创建一个包 import java.awt.Panel; //引入包 public class QuestionOne { //操作for循环问题的类 public static void main(String []args){ //Java程序的主入口处 Panel[] panels = new Panel[]{ p1,p2,p3,p4}; //声明一个数组 for (int i = 0 ; i < panels.length ; i++) { //循环对数组进行初始化 panels[i] = new Panel(); } } }
(3)运行结果如下所示:
编译不通过
源程序解读
语句Panel[]panels=new Panel[]{p1,p2,p3,p4};是想用匿名数组进行初始化,这样是不对的。如果是用匿名数组进行初始化可以这样:
Int []array=new int[]{1,2,3,4}; Panel []p=new Panel[]{new Pannel(),new Panel()};
基本数据类型那样初始化可以。