第4章 流程控制与数组
4.1 顺序结构
从上往下执行。没有流程控制的情况下,代码从第一行一直往下执行到最后一行,
4.2 分支结构
Java提供两种常见的分支结构:if语句和switch语句。
4.2.1 if条件语句
if语句使用布尔表达式或布尔值作为分支条件进行分支控制。if语句有3中形式:
if( expression){ //如果条件成立,则执行语句
statement...
}
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");
}
}
}