第4章 流程控制与数组
4.1 顺序结构
从上往下执行。没有流程控制的情况下,代码从第一行一直往下执行到最后一行,
4.2 分支结构
Java提供两种常见的分支结构:if语句和switch语句。
4.2.1 if条件语句
if语句使用布尔表达式或布尔值作为分支条件进行分支控制。if语句有3中形式:
if( expression){ //如果条件成立,则执行语句 statement... }
2.
if(expression){ //如果条件成立,则执行语句 statement... } else{ //否则,执行else中语句 statement... }
3
if(expression){ //如果条件成立,则执行语句 statement... } else if(expression){ //否则,如果下一个if成立执行else if中语句 statement... } //...可以有0个或多个else if语句 else{ //最后的else也可以省略 statement... }
花括号括起来的部分作为代码块,将多行代码构成整体。
为了可读性和减少错误,建议if else语句中使用花括号,即使代码块只有一行。
@if else 的逻辑错误
public class IfErrorTest { public static void main(String[] args){ int age = 45; if(age>20){ System.out.println("青年"); } else if(age>40){ System.out.println("中年"); } else if(age>60){ System.out.println("老年"); } } }
表面上没有问题,但是案例的45岁经过if(age>20)时会输出青年。
应该修改顺序,
if(age>60) //老年
else if(age>40)//中年
else if(age>20)//青年
在使用if else 时,有一条基本规则,应该优先处理包含范围小的条件。比如age>60和age>20,应该先处理范围较小的age>60。
4.2.2 Java 7 增强后的switch语句
switch语句由一个控制表达式和多个case标签组成。
switch的控制表达式只能是byte,short,char,int四个整数型,枚举、String类型(Java7开始允许)
switch语法:
switch(expression) { case condition1: statemet; break; } case condition2: { statemet; break; } ... default: { statemet; }
执行先对expression求值,然后依次匹配condition1、condition2、…遇到匹配的就执行对应的执行体。如果都不匹配,则执行default。
由于case标签使代码块非常清晰,可以省略花括号。
@注意不要漏写break,不然一遇到相等的值,程序就会一直执行完标签后面的所有语句。例如下面的例子,如果去掉break,程序输出夏天 秋天 冬天 季节输入错误。
例:
public class StringSwitchTest { public static void main(String\[\] args) { String season = "夏天"; switch(season) { case "春天": System.out.println("春天"); break; case "夏天": System.out.println("夏天"); break; case "秋天": System.out.println("秋天"); break; case "冬天": System.out.println("冬天"); break; default" System.out.println("季节输入错误"); } } }
4.3 循环结构
在满足条件时,反复执行一段代码(循环体)。
在执行循环体时,需要在某些时候将条件改为假,否则就会一直执行循环体,形成死循环。
循环语句可能包含4个部分:
初始化,循环条件,循环体,迭代语句。
4.3.1 while循环
while语句语法
[init_statement] while(expression) { statement; [iteration_statement]; }
例:
public class WhileTest { public static void main(String[] args) { int count =0; while(count<10) { System,out.println(count); } System.out.println("循环结束!"); } }
@while陷阱 while后面紧跟一个;时,while后面的代码块和while没有关系。
4.3.2 do while
do while和while的区别是do while是先do,在判断
[init] do{ statement; [iteration_statement]; }while(expression);
do while的循环条件后面必须有一个分号,表明循环结束。
4.3.3 for循环
for循环是更加简洁的循环语句,大部分情况下,都可以使用for循环。
for([init_statemnet]; [test_expression;[iteration_stetement]) { statement }
在执行循环前,先执行初始化语句init_stetement;
每次循环前,先计算test_expression的值,如果true,则执行循环体,然后执行循环迭代语句。
@for循环的循环迭代语句没有和循环体放在一起,因此即使循环体遇到continue结束本次循环,循环迭代语句也会执行。这是while、do while语句不能做到的。
例:for循环
public static void main(String[] args){ for(int count =0;count<10;count++){ System.out.println(count); } System.out.println("循环结束!"); }
4.3.4 循环嵌套
把一个循环放入另一个循环体内,就可以形成循环嵌套。
for(int i=0;i<5;i++){ for(int j=0;j<3;j++){ System.out.println("i="+i+"j="+j); }
4.4 控制循环结构
4.4.1 break结束循环
break用于完全结束一个循环,跳出循环体。
@break不仅可以结束其所在的循环,还可以直接结束外层循环,此时需要break后面跟一个标签,这个标签用于标识外层循环。
outer: for(int i=0;i<5;i++){ for(int j=0;j<3;j++){ Suytem.out.println("i="+i+"j="+j); if(j==1){ break outer; } }
4.4.2 使用continue忽略本次循环剩下语句
@与break类似,continue也可以跟一个标签,忽略标识循环的剩下语句,重新开始下一次循环。
4.4.3 使用return结束方法
return的功能是结束一个方法,如果循环在一个方法中,使用return结束方法,循环也随之结束。
4.5 数组类型
4.5.1 理解数组:数组也是一种类型
Java要求数组的数组元素具有相同的数据类型。
int[] 也是一种类型。
4.5.2 定义数组
type[] arrayName;
type arrayName[];
推荐第一种格式。
数组是一种引用类型的变量,定义时仅仅定义了一个引用变量,(也就是定义了一个指针),还没有指向有效内存。
只有初始化时后才能使用。
4.5.3 数组的初始化
1.静态初始化
arrayName = new type[] {element1,element2,…};
简化的格式
type[] arrayName = {element1,element2,…};
2.动态初始化
只指定数组长度,由系统指定初始值。
arrayName = new type[length];
@不要同时静态初始化和动态初始化。不要即指定长度,又同时指定初始值。
4.5.4 使用数组
访问元素arrayName[index]
索引是从0开始的。
数组提供了length属性,表示数组长度。可以利用length遍历数组
for(int i=0;i<prices.length;i++){ System.out.println(prices[i]); }
4.5.5 foreach循环
用于遍历数组和集合。
for(type variableName : array|collection) { //varivableName自动迭代访问每个元素 }
例子:
// String[] books = {"Bo1","Bo2"}; for(String book : books) { System.out,println(book); }
@for each循环不能改变数组元素的值,因此不要用foreach进行赋值。
4.6 深入数组
4.6.1 内存中的数组
数组引用变量是一个引用,这个引用变量可以指向任何有效内存,然后访问数组元素。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LVPO4uLo-1588335970117)(media/image1.png)]{width=“5.433566272965879in” height=“3.0264884076990377in”}
只要类型兼容,可以让一个数组变量指向另一个实际的数组,这种操作会让人产生数组长度可变的错觉,但其实数组没变,只是引用变量指向另一个数组。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d38TXVTh-1588335970119)(media/image2.png)]{width=“4.286713692038496in” height=“4.588170384951881in”}
4.6.2 基本类型数组的初始化
int [] iArr; iArr = new int[5]; for(int i=0;i<iArr.length; i++){ iArr[i] = i+10; }
4.6.3 引用类型数组的初始化
数组元素是引用时,情况变得复杂。
//Person类 //定义Person数组 Person[] students; students = new Person[2]; //创建Person实例zhang Person zhang = new Person(); zhang,age = 15;zhang,height = 158; //创建Person实例Lee Person lee = = new Person(); lee.age = 16;lee.height = 160; //赋值给数组元素 student[0] = zhang; student[1] = lee;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sLpVP9qC-1588335970123)(media/image3.png)]{width=“4.083916229221347in” height=“2.094316491688539in”}
4.6.4 “没有多维数组”
Java提供了支持多维数组的语法,但从数组底层来说,只有一维数组。
int[][] arrName; //二维数组的定义
它的本质还是一维数组,数组元素也是引用,元素保存的引用指向一维数组。
接着对二维数组进行初始化:
arrName = new type[length][];
同样可以当成是一维数组的初始化,这个一维数组的元素是引用类型(数组类型)的。
4.6.5 Java8增强的工具类 Arrays
Arrays类包含一些static方法可以直接操作数组。
二分查找binarySearch
复制数组 copyOf
判断相等equals
填充数组fill
排序 sort
转换字符串 toString
int binarySearch(type[] a, type key): 二分查找key在a数组出现的索引,如果不包含key,则返回负数。要求数组元素已经升序排列。
int binarySearch(type[] a,int fromIndex, int toIndex, type key) 只搜索从fromIndex到 toIndex的元素
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hx3uaxsu-1588335970128)(media/image4.png)]{width=“8.139860017497814in” height=“4.137016622922134in”}
@Arrays类 ,使用需要import java.util.Arrays类。
java8 为Arrays类增加的工具方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-61ThhsbZ-1588335970129)(media/image5.png)]{width=“7.311807742782152in” height=“4.916083770778653in”}
@parallel代表并行
Arrays类使用举例:
import java.util.Arrays; public class ArraysTest { public static void main(String[] args){ int [] a = new int[]{3,4,5,6}; int [] a2 = new int[]{3,4,5,6}; System.out.println("a和a2是否相等:"+Arrays.equals(a,a2)); int []b = Arrays.copyOf(a,6); System.out.println("a和b是否相等:"+Arrays.equals(a,b)); System.out.println("b数组元素:"+Arrays.toString(b)); Arrays.fill(b,2,4,1); System.out.println("b数组元素:"+Arrays.toString(b)); Arrays.sort(b); System.out.println("b数组元素:"+Arrays.toString(b)); } }
Arrays类新增方法举例:
import java.util.*; import java.util.function.IntBinaryOperator; import java.util.function.IntUnaryOperator; public class ArraysTest2 { public static void main(String[] args) { int[] arr1 = new int[] { 3, -4, 25, 16, 30, 18 }; Arrays.parallelSort(arr1); System.out.println(Arrays.toString(arr1)); int[] arr2 = new int[] { 3, -4, 25, 16, 30, 18 }; Arrays.parallelPrefix(arr2, new IntBinaryOperator() { public int applyAsInt(int left, int right) { return left * right; } }); System.out.println(Arrays.toString(arr2)); int[] arr3 = new int[5]; Arrays.parallelSetAll(arr3,new IntUnaryOperator(){ @Override public int applyAsInt(int operand) { return operand*5; } }); System.out.println(Arrays.toString(arr3)); } }
4.6.6 数组的应用举例
如果程序中有多个类型相同的变量,且它们具有逻辑的整体性,则可以将它们定义成一个数组。
例如,开发一个工具函数:将一个浮点数转换成人民币读法字符串,这个程序就很需要使用数组,
思路是将浮点数分成整数和小数部分,整数部分使用整数强制转换,小数部分用浮点数-整数部分即可。
整数部分需要考虑中国的数字习惯,4位一节,1个4位数字可以转换成几千几百几十几。
除此之外,还可以利用二维数组实现五子棋,连连看,俄罗斯方块等小游戏。
//五子棋,仅实现棋盘,胜负判定未实现
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.*; public class Gobang { //棋盘大小 private static int BOARD_SIZE = 15; //二维数组当棋盘 private String[][] board; //初始化棋盘 public void initBoard(){ board = new String[BOARD_SIZE][BOARD_SIZE]; for(int i=0;i<BOARD_SIZE;i++){ for(int j=0;j<BOARD_SIZE;j++){ board[i][j] = "+"; } } } //控制台输出棋盘 public void printBoard(){ for(int i=0;i<BOARD_SIZE;i++){ for(int j=0;j<BOARD_SIZE;j++){ System.out.print(board[i][j]); } System.out.print(\"\n"); } } public static void main(String[] args) throws Exception{ Gobang gb = new Gobang(); gb.initBoard(); gb.printBoard(); //获取键盘输入 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String inputStr = null; while(( inputStr=br.readLine())!=null){ String[] posStrArr = inputStr.split(","); int xPos = Integer.parseInt(posStrArr[0]); int yPos = Integer.parseInt(posStrArr[1]); gb.board[yPos-1][xPos-1] = "#"; gb.printBoard(); System.out.println("请输入下棋的坐标: x,y"); } } }
附录 本章练习
1.使用循环打印九九乘法表
//九九乘法表 package ch4; public class Test1 { public static void main(String[] args) { for(int i=1;i<=9;i++) { for(int j=1;j<=i;j++) { System.out.print(i+"*"+j+"="+i*j+" "); } System.out.print("\n"); } } }
2.使用循环输出等腰三角形。
//打印rows行的等腰三角形 package ch4; public class Test2 { public static void main(String[] args) { final int rows = 4; for(int i=1;i<=rows;i++) { for(int j=0;j<rows-i;j++) { System.out.print(" "); } for(int j=0;j<2*i-1;j++) { System.out.print("*"); } System.out.print("\n"); } } }
3.打印近似圆
//画圆 package ch4; public class Test3 { public static void main(String[] args) { int r = 10 ;//半径 for(int y=0;y<=2*r;y+=2) { //y+=2,如果是y++的话就会(因为精度)很难看 int x1 = r-getX(r,y); int x2 = r+getX(r,y); for(int j=0-r;j<x1;j++) { System.out.print(" "); } System.out.print("*"); for(int j=x1;j<x2;j++) { System.out.print(" "); } System.out.print("*"); System.out.print("\n"); } } public static int getX(int r,int y) { double tempX; tempX = Math.sqrt(r*r - (y-r)*(y-r)); return (int)Math.round(tempX); } }
4.按字节截取字符串的子串。类似String类的substring()。
感觉这个题目有问题,略过
5.编写一个程序,将浮点数转换成人民币读法字符串,精确到分且不超过12位。例如。将1006.333转换为壹仟零陆元叁角叁分
重点是转换规律是每4位一转,然后根据位置加上元/万/亿。
另一个易错点是零的处理,连续零和末尾零以及全零数。
package ch4; public class Test5 { public static String[] hanArr= {"零","壹","贰","叁","肆","伍","陆","柒","捌","玖"}; public static String[] unitArr= {"十","百","千"}; public static String divide(double num) { long zheng = (long )num;//整数部分 long xiao = Math.round((num-zheng)*100); //保留两位小数 return new String(zheng+"#"+xiao); } //将4位及4位以内数字字符串转换成人民币读法 public static String trans4(String s) { int len = s.length(); if(len>4&&len<1) { System.out.println("传入参数错入,应该传入1-4位的数字字符串"); return ""; } String tmp = s; String result = ""; if("0000".equals(tmp)||"000".equals(tmp)||"00".equals(tmp)|| "0".equals(tmp)){ return result = hanArr[0]; } //去除前面的0 while(tmp.charAt(0)=='0'&&len>1) { tmp = tmp.substring(1,len); len = len-1; } //转换... for(int i=0;i<len;i++) { int n = tmp.charAt(i)-48; //不为0且不是最后一位,直接转为数字+单位 if( n !=0 && (i!=len-1)) { result+= hanArr[n]+unitArr[len-2-i]; }else if(n==0) { //判断连续0,前面不是0时加'零',有连续的0过就跳过,末尾的0也跳过 if((tmp.charAt(i-1)-48)!=0 && i!=len-1) { result+=hanArr[0]; } }else if(n!=0&&(i==len-1)) { result+=hanArr[n]; } } //末尾零,去掉 if(result.charAt(result.length()-1)=='零') { result = result.substring(0,result.length()-1); } return result; } public static String transAll(String s) { String result = ""; //4位数以内 if(s.length()<=4) { result = trans4(s); } //4位数以上8位数以内 else if(s.length()<=8) { String s1 = trans4(s.substring(0,s.length()-4));//高位 String s2 = trans4(s.substring(s.length()-4));//低位 if("零".equals(s2)) { s2=""; } result = s1 +"万"+ s2; } else if(s.length()<=12) { String s1 = trans4(s.substring(0,s.length()-8));//高位 String s2 = trans4(s.substring(s.length()-8,s.length()-4)) ;//低位 String s3 = trans4(s.substring(s.length()-4)); //最低位 result += s1 +"亿"; if(!"零".equals(s2)) { result += s2+"万"; } if(!"零".equals(s3)) { result += s3; } } return result; } public static void main(String[] args) { double num = 12301234000.11; String snum = divide(num); String zheng = snum.substring(0, snum.indexOf("#")); String xiao = snum.substring(snum.indexOf("#")+1); //小数部分简单, 直接写在这里了 String han_xiao =""; if(xiao.length()==1) { char c=xiao.charAt(0); han_xiao = hanArr[(c-48)]+"角"; }else if(xiao.length()==2) { char c=xiao.charAt(0); han_xiao += hanArr[(c-48)]+"角"; c = xiao.charAt(1); han_xiao += hanArr[(c-48)]+"分"; } System.out.println(transAll(zheng)+" "+han_xiao); } }
6.控制台五子棋
/*实现了双人对战,未处理非法输入, 因为没找到合适的字符棋盘有点难看 */ package ch4; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.*; public class Gobang { private static int BOARD_SIZE = 15; //棋盘大小 private String[][] board; //二维数组当棋盘 //初始化棋盘 public void initBoard(){ board = new String[BOARD_SIZE][BOARD_SIZE]; for(int i=0;i<BOARD_SIZE;i++){ for(int j=0;j<BOARD_SIZE;j++){ board[i][j] = "十"; } } } //控制台输出棋盘 public void printBoard(){ for(int i=0;i<BOARD_SIZE;i++){ for(int j=0;j<BOARD_SIZE;j++){ System.out.print(board[i][j]); } System.out.print("\n"); } } public boolean judge(int y,int x,String s) { int left = (x-4>=0)?(x-4):0; int right = (x+4<BOARD_SIZE)?x+4:BOARD_SIZE; int up = (y-4>=0)?(y-4):0; int down = (y+4<BOARD_SIZE)?y+4:BOARD_SIZE; int tmpx = x; int tmpy = y; int zx = x; int zx2 = x; int zy = y; int zy2 = y; while(s.equals(board[y][tmpx])&&tmpx>left) { tmpx--; } while(s.equals(board[tmpy][x])&&tmpy>up){ tmpy--; } while(s.equals(board[zy][zx])&&zx>left&&zy>up) {//左上 zy--; zx--; } while(s.equals(board[zy2][zx2])&&zx>left&&zy<down) {//左下 zy2++; zx2--; } int countx=0; int county=0; int countz1 = 0; int countz2 = 0; //x轴y轴判断 for(int i=tmpx;i<=right;i++) { if(s.equals(board[y][i])) { countx++; } } for(int j=tmpy;j<=down ;j++) { if(s.equals(board[j][x])) { county++; } } //斜线判断 for(int j=zy,i=zx;j<=down&&i<=right;j++,i++) { if(s.equals(board[j][i])) { countz1++; } } for(int j=zy2,i=zx2;j>=up&&i<=right;j--,i++) { if(s.equals(board[j][i])) { countz2++; } } if(countx>=5||county>=5||countz1>=5||countz2>=5) { return true; } return false; } public static void main(String[] args) throws Exception{ Gobang gb = new Gobang(); gb.initBoard(); gb.printBoard(); //获取键盘输入 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String inputStr = null; System.out.println("请输入下棋的坐标: x,y"); boolean meWin = false; boolean youWin = false; boolean myTurn = true; while(( inputStr=br.readLine())!=null ){ String[] posStrArr = inputStr.split(","); int xPos = Integer.parseInt(posStrArr[0]); int yPos = Integer.parseInt(posStrArr[1]); if(myTurn) { gb.board[yPos-1][xPos-1] = "●"; meWin = gb.judge(yPos-1,xPos-1,"●"); } else { gb.board[yPos-1][xPos-1] = "○"; youWin = gb.judge(yPos-1,xPos-1,"○"); } gb.printBoard(); if(meWin) { System.out.println("meWin!"); break; }else if(youWin) { System.out.println("youWin!"); break; } myTurn = !myTurn; System.out.println("请输入下棋的坐标: x,y"); } } }