Java学习-03 Java语句的执行结构及数组学习

Java学习-03 Java语句的执行结构及数组学习

1、Java语句的执行结构

Java程序是由语句组成的。语句能够通过创建和操作变量、对变量赋值并控制这些操作的执行流程来描述运算。语句通常会被组织成代码段,即花括号中的一系列语句。首先来大概说一说Java中的语句分类:

  • 声明语句:创建某种类型的变量并用标识符为其命名。
  • 赋值语句:将(由表达式产生的)某种类型的数值赋予一个变量。
  • 条件语句:能够简单地改变执行流程—根据指定的条件选择执行两个代码段之一。
  • 循环语句:更彻底的改变执行流程—只要条件为真就不断地反复执行代码段中的语句。
  • 调用和返回语句:和静态方法有关,是改变执行流程和代码组织的另一种方式。

1996 年,计算机科学家 Bohm 和 Jacopini 证明了:任何简单或复杂的算法都可以由顺序结构、分支结构和 循环结构这三种基本结构组合而成。
它们的共同点是都包含一个入口和一个出口,它们的每个代码都有机会被执行,不会出现死循环。

1.1、顺序结构

顺序结构是一种基本的控制结构,它按照语句出现的顺序执行操作。

1.2、分支结构

分支结构又被称为选择结构,根据条件成立与否来执行操作。

1.2.1、if条件语句

if条件结构是根据条件判断之后再做处理,如果条件为真,则执行if之后的花括号内的代码,否则就执行else或者else if的花括号中的代码。如果没有else或者else if 那么当条件不满足的时候就直接跳过,执行后边的语句。
if(条件语句){…}
if (条件语句){…}else{…}
if (条件语句){…}else if(条件语句){…}
if (条件语句){…}else if(条件语句){…}else{…}

举个栗子,我们应该都考过试,学校按照我们的考试分数把成绩分为优秀(>=90),良好(80-90),合格(70-80),及格(60-70),不及格(<60),那么用程序可以描述为:

import java.util.Scanner;
public class Grade{
	public static void main(String[] args){
		Scanner input = new Scanner(System.in);
		System.out.println("请输入成绩:");
		int score s= input.nextInt();
		// 90以上优 80-90 良 70-80 合格 60-70 及格 <60 不及格
		if(score >= 90){
			System.out.println("您的成绩是优秀!不要骄傲继续加油哦!");
		}else if(score >= 80 & score < 90){
			System.out.println("您的成绩是良好!还要努力哦!");
		}else if(score >= 70 & score < 80){
			System.out.println("您的成绩是合格!还要加倍努力哦!");
 		 else if(score >= 60 & score < 70){
			System.out.println("您的成绩是及格!差一点点就不及格,下次要更努力了哦!");
		}else{System.out.println("您的成绩不合格!下次可不能不好好听课了!");
		}
	}
}
   
1.2.2、switch语句

一般格式:

switch(表达式){
case 取值 1: 语句块 1;break;
case 取值 n: 语句块 n;break;
default: 语句块 n+1;break;
}

switch 语句有关规则
表达式的返回值必须是下述几种类型之一:byte, int, char, short, String;
case 子句中的取值必须是常量,且所有 case 子句中的取值应是不同的;
default 子句是可选的;
break 语句用来在执行完一个 case 分支后使程序跳出 switch 语句块;如果 case 后面没有写 break 则直接往下面执行!
case 后面的执行体可写{ }也可以不写{ }.

这种选择语句一般用于case特别多的场景,举个栗子,我们想知道某一年某一月有多少天,可以如下编写代码:

import java.util.Scanner;

public class CalculationDays {
    public static void main(String[] args) {
        /*
        一年中有 12 个月,而每个月的天数是不一样的。其中大月 31 天,分别为
        1,3,5,7,8,10,12 月,小月 30 天,分别 为 4,6,9,11 月。还有二月比较特殊,平
        年的二月只有 28 天,而闰年的二月有 29 天.在控制台输入年份和月份,程序计算该年该月的天数。
        判断闰年条件①:非整百年数除以4,无余为闰,有余为平;②整百年数除以400,无余为闰有余平。
         */
        Scanner input = new Scanner(System.in);
        System.out.println("请输入年份:");
        int year = input.nextInt();
        System.out.println("请输入月份:");
        int month = input.nextInt();
        switch (month) {
            case (1):
            case (3):
            case (5):
            case (7):
            case (8):
            case (10):
            case (12):
                // 1,3,5,7,8,10,12月份有31天
                System.out.println(year + "年" + month + "月有31天");
            case (4):
            case (6):
            case (9):
            case (11):
                // 4,6,9,11月有30天
                System.out.println(year + "年" + month + "月有30天");
            case (2):
                // 判断是否为闰年
                if (year % 400 == 0 | (year % 100 != 0 & year % 4 == 0)) {
                    // 闰年2月有29天
                    System.out.println(year + "年" + month + "月有29天");
                } else {
                    // 平年2月有28天
                    System.out.println(year + "年" + month + "月有28天");
                }
        }
    }

1.3、循环结构

循环结构是一种重复结构,如果条件成立,它会重复执行某一循环体,直到出现不满足的条件为止。

循环语句分类:

  • for 循环
  • while 循环
  • do/while 循环
1.3.1、while循环

首次先判断是否满足条件,符合条件则执行循环体,然后再判断,符合条件,循环继续执行;否则,循环退出。
特点:先判断,再执行

格式:

while(条件表达式){
	//循环体;
}

使用 while 循环的步骤:

1、分析循环条件和循环操作

2、套用 while 语法写出代码

3、检查循环是否能够退出(重要)

举个栗子,计算1+2+3+4+…+n(n个数字之和):

int sum = 0;
int n = 10;
int i = 0;
while(i <= n ){
    sum += i;
    i++;
}
System.out.print("1+2+3+...+10的结果为:" + sum);
1.3.2、do-while 循环

首次先执行一遍循环操作,符合条件,循环继续执行;否则,循环退出。
特点:先执行,再判断

格式:

do {
	// 循环题
}while ( 条件表达式 );

因此while 循环和 do-while 循环的区别就是:
while:先判断条件,如果条件满足,再执行循环操作。
do while:先执行一遍循环操作,然后再判断条件,如果条件满足,继续执行循环操作。

举个栗子,之前网上流传一个价值一个亿的人工智能代码:

import java.util.Scanner;

public class AI {
    public static void main(String[] args) {
        // 人工智障的精髓,把"吗"去掉,把"我"变成"我也",把?变成!,还可以有其他形式,感兴趣的自己玩玩
        // 实例化一个Scanner对象,用来接收输入
        Scanner input = new Scanner(System.in);
        System.out.println("请输入聊天内容:");
        String question, reply;
        do {
            question = input.next();
            reply = question.replace("吗", "");
            reply = reply.replace("我", "我也");
            reply = reply.replace("?", "!");
            System.out.println(reply);
        } while (!reply.equals("滚"));
    }
}

/*
在吗?
在!
吃了吗?
吃了!
我爱你
我也爱你
能听得懂吗?
能听得懂!
真的吗?
真的!
*/

1.3.3、for循环

格式:

for( 初始化参数;判断条件;更新循环变量){
	// 循环体;
}

举个栗子,打印我们小学背过的九九乘法表:

public class MultiplicationTable {
    public static void main(String[] args) {
        System.out.println("乘法口诀表:");
        // 打印乘法口诀表
        for (int i = 1; i <= 9; i++) {
            for (int j = 1; j <= i; j++) {
                System.out.print(j + "*" + i + "=" + (i * j) + "\t");
            }
            System.out.println();
        }
    }
}

1.3.4、退出循环

最后说一下break和continue语句。break语句是直接跳出当前所在的循环(不执行循环体中break之后的语句),不再执行循环,而continue则是跳过这一次的循环(不执行循环体中continue之后的语句),继续执行之后下一次循环的判断和循环。看例子:

public class Demo {

/*
	break与continue语句:
	 	break是用于终止本轮所有次循环。即不执行本次循环中break后面的语句,直接跳出循环。
	  	continue是用于终止本轮本次循环。即本次循环中continue后面的代码不执行,进行下一次循环的入口判断。
	 
 */
	public static void main(String[] args) {
		for(int i=0;i<10;i++) {
			if(i==5) {
				break;
			}
			System.out.println(i);
		}
        System.out.println("-------------------------");
        for(int j=0;j<10;j++) {
			if(j==5) {
				continue;
			}
			System.out.println(j);
		}
	}

}

运行之后可以发现,break的代码,只输出到0,1,2,3,4,continue的代码则是没有打印5.

有时候有多个循环嵌套,一个break只能退出它所在的循环,那怎么直接跳出所有循环呢?

通过给for循环起名字的方式,可以实现,参考代码如下:

public class Demo10 {

	/*
	  对循环起名字退出多重循环
	 */
	public static void main(String[] args) {
		loop1:for(int i=0;i<10;i++) {
			for(int j=0;j<10;j++) {
				System.out.println("i="+i+" , j="+j);
				if(i==5 && j==5) {
					break loop1;
				}
			}
		}
	}
}

2、Java中的数组

数组是相同数据类型的多个数据的容器。
这些元素按线性顺序排列。所谓线性顺序是指除第一个元素外,每一个元素都有唯一的前驱元素;除最后一个元素外,每一个元素都有唯一的后继元素。(“简单理解就是:一个跟一个顺序排列”)。

创建格式(一维数组):

格式 1. 数据类型[] 数组名称 = new 数据类型[数组长度];

格式 2. 数据类型[] 数组名称 = {数组内容 1,数组内容 2,数组内容 3…数组内容 n};

格式 3. 数据类型[] 数组名;

​ 格式 3 属于只创建了数组引用名, 并未在内存创建数组空间。

格式 4. 数据类型[] 数组名称 = new 数据类型[]{内容 1,内容 2,内容 3…内容 n};

一般我们常用的是格式1和格式2。

下标(索引)

可以理解为数组中内容的数字序号,从 0 开始 ,对于长度为 n 的数组,下标的范围是 0~n-1(尤其注意,往往会出现下标越界,不要记错范围)
可以通过下标的方式访问数组中的每一个元素。

例如: 创建 int 类型数组 nums , 给数组 nums 的 5 下标赋值数据 , 然后打印

int[] nums = new int[10];
nums[1] = 88;
System.out.println(nums[1]);

数组长度获取:

数组名称.length

使用数组不当, 会出现如下问题:

  • 数组未赋值: 空指针异常
  • 超出长度的下标操作: 数组越界异常
  • 注意:数组的长度在创建时就固定了。

最后说一下二维数组的创建,与一维数组类似:

// 数据类型[][] 数组名称 = new 数据类型[数组长度(必填)][选填];
// 注意的是必填的是必须填写的,后边的选填,如果填写了,那么每一个外层数组里面都是一样的数据量,然后可以通过下标进行赋值。如果不填,则需要动态申请nums[0] = new int[]{1,2,3};
int[][] nums = new int[5][];
nums[0] = new int[]{1,2,3};
nums[1] = new int[]{1,2,3,4,5};
System.out.println(nums[0][2]);
System.out.println(nums[1][3]);

/*
 int[][] nums = new int[5][3];
 nums[0][2] = 1;
 // 下面这一句会报错,因为初始化的时候,限定了长度为3
 nums[1][4]= 2;
 System.out.println(nums[0][2]);
 System.out.println(nums[1][4]);
*/

3、案例过程学习记录

1、一维数组练习—选队长游戏

规则如下:

同学们相约一起爬山游玩,为了更好的进行这场活动,大家准备推举一个人作为出游的临时队长。为了体现合理公平,大家提出了一个比较有趣的规则。所有人围成一圈,顺序排号。从第一个人开始报数(从1 到 3 报数),凡报到 3 的人退出圈子,剩下的人继续报数,最后留下的当选为队长。请编写一个程序,求出一组人中的队长是原来第几位同学。

这个问题其实是约瑟夫问题的变种。
那么代码如下:

import java.util.Scanner;

public class ChooseCaptain {
    public static void main(String[] args) {
         /*
        选队长游戏规则:
             所有人围成一圈,顺序排号。从第一个人开始报数(从 1 到 3 报数),
            凡报到 3 的人退出圈子,剩下的人继续报数,最后留下的当选为队长。
         求出一组人中的队长是原来第几位同学。
         */
        Scanner input = new Scanner(System.in);
        System.out.println("请输入共有多少位同学:");
        int nums = input.nextInt();
        // 初始化一个长度为nums的boolean数组,初始值,默认为false 表示没出局
        boolean[] classmates = new boolean[nums];
        // 定义计数器count记录1,2,3状态,初始为1
        int count = 1;
        // 定义一个out数组,记录每次出局的人
        int[] out = new int[nums - 1];
        // 定义一个k=0,用来标记第几轮淘汰的是哪一位同学,
        int k = 0;
        // 一定要淘汰 nums-1个人,所以 k < nums -1 ,不够,就继续报数,直到只剩一个人
        while(k < nums - 1){
            // 每一次遍历数组classmates,模拟报数
            for (int i = 0; i < nums; i++) {
                // 每一次,只有没有被淘汰的人才能报数,也就是他在classmates中状态是false
                if (!classmates[i]) {
                    // 如果报数为3 ,则淘汰,即把状态改为true,同时在out数组记录
                    if (count == 3) {
                        classmates[i] = true;
                        out[k] = i + 1;
                        k++;
                        // 输到3之后,该从1继续
                        count = 1;
                        // 如果状态为true,说明淘汰了,那么对count + 1,这是下一个人的报数
                    } else {
                        count++;
                    }
                }
            }
        }
        // 遍历输出每一轮淘汰的同学
        for (int i = 0; i < nums - 1; i++) {
            System.out.println("第" + (i + 1) + "轮出局的是第" + out[i] + "位同学");
        }
        // 最后classmates中,状态为false的同学为队长
        for (int i = 0; i < nums; i++) {
            if (!classmates[i]) {
                System.out.println("队长是原来的" + (i + 1) + "号同学!恭喜这个b");
                break;
            }
        }
    }

}

在这个案例中我学习到了使用计数器来模拟报数,并且在为计数器为3之后,就归1。第二个是,学到了用一个boolean数组来标记每个人的状态,这样在遍历数组的时候,直接查看这个人状态就知道是否要跳过。

2、二维数组练习—简单五子棋

规则如下:

  1. 绘制棋盘
  2. 提示黑方(用 1 表示)和白方(用 2 表示)分别下棋(X,Y 轴位置) 并重新绘制棋盘。
  3. 每当一方下棋后判断是否获胜 。

棋盘如下:
Java学习-03 Java语句的执行结构及数组学习_第1张图片
代码如下:

import java.util.Scanner;

public class GoBang {

    public static void main(String[] args) {
        /*
        1. 绘制棋盘
        2. 提示黑方(用 1 表示)和白方(用 2 表示)分别下棋(X,Y 轴位置)
           并重新绘制棋盘。
        3. 每当一方下棋后判断是否获胜 。
        4. 采用二维数组来模拟棋盘。
         chessBoard[0] = new int[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
        0123456789101112131415
        0000000000000000
        0000000000000000
        可以发现,第一个是行索引,第二个是列索引 所以我们用x表示列,y表示行,符合坐标轴的习惯
        5、难点主要在于对获胜的判断
         */

        // 定义一个方法实时绘制棋盘
        // 先定义棋盘,并初始化边界索引
        int[][] chessBoard = new int[16][16];
        chessBoard[0] = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
        for (int i = 0; i < 16; i++) {
            chessBoard[i][0] = i;
        }
        // 初次绘制棋盘
        paintChessBoard(chessBoard);

        // 下边的代码要重复运行直到出现胜者,因此最好封装成方法
        // 然后发现,大部分代码都一样,因此调整,加参数标记黑白
        // 提示黑方下棋  黑色棋子用1,白色棋子用2
        Scanner input = new Scanner(System.in);
        // 初始化x,y坐标
        int x, y;
        // 定义一个布尔变量记录上一步白方是否落子
        boolean white;
        // 定义一个布尔变量判断是否越界
        boolean outside = false;
        // 初始为黑方先下
        int marker = 1;
        String text;
        while (true) {
            if (marker == 1) {
                text = "黑";
                white = false;
            } else {
                text = "白";
                white = true;
            }
            System.out.println("请" + text + "方落子:");
            System.out.println("输入X坐标:");
            if (input.hasNextInt()) {
                do {
                    x = input.nextInt();
                    System.out.println("输入Y坐标:");
                    y = input.nextInt();
                    if (x < 1 || y < 1 || x > 15 || y > 15 ) {
                        System.out.println("那里是边界,不可以落子!请重新输入!");
                        System.out.println("请" + text + "方落子:");
                        System.out.println("输入X坐标:");
                        outside = true;
                    } else if (chessBoard[y][x] != 0) {
                        System.out.println("哪里已经有棋子了!请重新输入!");
                        System.out.println("请" + text + "方落子:");
                        System.out.println("输入X坐标:");
                    }else{
                        outside = false;
                    }
                } while (outside || chessBoard[y][x] != 0 );
//                outside = false;
                chessBoard[y][x] = marker;
                paintChessBoard(chessBoard);
                // 判断是否有赢家
                if (isWin(chessBoard, x, y, marker)) {
                    System.out.println(text + "方赢!");
                    break;
                }
                // 更改marker
                marker = white ? 1 : 2;
            } else {
                System.out.println("输入了错误的格式,请重新开始游戏!");
                break;
            }
        }
    }

    public static void paintChessBoard(int[][] chessBoard) {
        System.out.println("------------------------------------------------------------------");
        System.out.println("  —X ");
        System.out.println("|");
        System.out.println("Y");
        for (int i = 0; i < 16; i++) {
            for (int j = 0; j < 16; j++) {
                System.out.print(chessBoard[i][j] + "\t");
            }
            System.out.println(" ");
        }
        System.out.println("------------------------------------------------------------------");
    }

    public static boolean isWin(int[][] chessBoard, int x, int y, int marker) {
        // 判断横向的是否有5个棋子相连,即横坐标是相同的,即chessBoard[y][x]中y值是相同的
        // 判断纵向的是否有5个棋子相连,即纵坐标是相同的,即chessBoard[y][x]中x值是相同的
        // 判断斜向的是否有5个棋子相连,即纵横坐标是相差1的,即chessBoard[y][x]中x,y各差1
        return checkCount(chessBoard, 1, 0, x, y, marker) >= 5
                || checkCount(chessBoard, 0, 1, x, y, marker) >= 5
                || checkCount(chessBoard, 1, 1, x, y, marker) >= 5
                || checkCount(chessBoard, 1, -1, x, y, marker) >= 5;
    }

    // 判断棋子连接的数量
    public static int checkCount(int[][] chessBoard, int xChange, int yChange, int x, int y, int marker) {
        int count = 1;
        int tempX = xChange;
        int tempY = yChange;

        // 先向一个方向搜索
        // 不能超出边界 而且要注意 y是行,x是列
        while (x + xChange >= 1 && x + xChange <= 15 && y + yChange >= 1 && y + yChange <= 15
                && marker == chessBoard[y + yChange][x + xChange]) {
            count++;
            if (xChange != 0)
                xChange++;
            if (yChange != 0) {
                if (yChange > 0)
                    yChange++;
                else {
                    yChange--;
                }
            }
        }
        // 向另一个方向搜索
        xChange = tempX;
        yChange = tempY;
        while (x - xChange >= 1 && x - xChange <= 15 && y - yChange >= 1 && y - yChange <= 15
                && marker == chessBoard[y - yChange][x - xChange]) {
            count++;
            if (xChange != 0)
                xChange++;
            if (yChange != 0) {
                if (yChange > 0)
                    yChange++;
                else {
                    yChange--;
                }
            }
        }
        return count;
    }
}

运行截图:
Java学习-03 Java语句的执行结构及数组学习_第2张图片
学习记录:

二维数组的第一个是行索引,第二个是列索引

五子棋的练习中,为了让用户输入正确的格式和整数范围,我使用了hasNextInt()方法,和if判断,并使用了一个布尔变量记录是否越界,然后发现,程序出现了问题,经过排查,发现是,我在把越界设置成true之后,没有在下次用户输入了合适的数据后,把它变成false,导致出现奇怪的现象。

还有就是,我在判断是否那个位置有棋子和越界上遇到了问题,我的if先判断了是否由棋子,然后用else if 判断了是否越界,结果就会运行出错,无法正常判断越界,然后我意识到是次序的原因。经过这个练习,我深刻的了解了判断次序的重要性,还有就是意识到了短路与,短路或的好处,当条件较多时,用这个很不错。

而获胜的判断,较为繁琐,但是仔细捋程序就能捋通。

经过这次的清除BUG,我只能说,当遇到问题的时候,仔细一句一句的跟着捋程序,一些问题就迎刃而解了。

你可能感兴趣的:(学习笔记,java,编程语言)