程序设计中规定的三种流程结构,即:

  • 顺序结构
    • 程序从上到下逐行地执行,中间没有任何判断和跳转。
  • 分支结构
    • 根据条件,选择性地执行某段代码。
    • if…elseswitch-case两种分支语句。
  • 循环结构
    • 根据循环条件,重复性的执行某段代码。
    • forwhiledo-while三种循环语句。
    • 补充:JDK5.0 提供了foreach循环,方便的遍历集合、数组元素。(第12章集合中讲解)

分支语句

if-else条件判断结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
if(条件表达式)
  	语句块;


if(条件表达式) { 
  	语句块1;
}else {
  	语句块2;
}

if (条件表达式1) {
  	语句块1;
} else if (条件表达式2) {
  	语句块2;
}
...
}else if (条件表达式n) {
 	语句块n;
} else {
  	语句块n+1;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class IfElseExer4 {
    public static void main(String[] args) {
        System.out.print("小明的期末Java成绩是:[0,100]");
        int score = 89;

        if (score < 0 || score > 100) {
            System.out.println("你的成绩是错误的");
        } else if (score >= 90) {
            System.out.println("你的成绩属于优秀");
        } else if (score >= 80) {
            System.out.println("你的成绩属于好");
        } else if (score >= 70) {
            System.out.println("你的成绩属于良");
        } else if (score >= 60) {
            System.out.println("你的成绩属于及格");
        } else {
            System.out.println("你的成绩属于不及格");
        }

    }
}

当条件表达式之间是“互斥”关系时(即彼此没有交集),条件判断语句及执行语句间顺序无所谓。

当条件表达式之间是“包含”关系时,“小上大下 / 子上父下”,否则范围小的条件表达式将不可能被执行。

if…else嵌套

 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
class IfElseTest4 {
	public static void main(String[] args) {
		
			//声明num1,num2,num3三个变量并赋值
			int num1 = 23,num2 = 32,num3 = 12;

			if(num1 >= num2){
				
				if(num3 >= num1)
					System.out.println(num2 + "-" + num1 + "-" + num3);
				else if(num3 <= num2)
					System.out.println(num3 + "-" + num2 + "-" + num1);
				else
					System.out.println(num2 + "-" + num3 + "-" + num1);
			}else{ //num1 < num2
				
				if(num3 >= num2){
					System.out.println(num1 + "-" + num2 + "-" + num3);
				}else if(num3 <= num1){
					System.out.println(num3 + "-" + num1 + "-" + num2);
				}else{
					System.out.println(num1 + "-" + num3 + "-" + num2);
				}
			}
	}
}
  • 语句块只有一条执行语句时,一对{}可以省略,但建议保留
  • 当if-else结构是“多选一”时,最后的else是可选的,根据需要可以省略
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//1)对下列代码,若有输出,指出输出结果。
int x = 4;
int y = 1;
if (x > 2) {
       if (y > 2) 
            System.out.println(x + y);
    		//if没有括号只包含一行,下一行在if结构外。
       		System.out.println("hello");
    	//System.out.println("hello");//相当于在这
} else
       System.out.println("x is " + x);
//hello
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
boolean b = true;
//if(b=false) //赋值b为false,不满足 c
if(b == false)//判断b为false b	 //建议:if(!b)
	System.out.println("a");
else if(b)
	System.out.println("b");
else if(!b)
	System.out.println("c");
else
	System.out.println("d");

switch-case选择结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
switch(表达式){
    case 常量值1:
        语句块1;
        //break;
    case 常量值2:
        语句块2;
        //break; 
    // ...
   [default:
        语句块n+1;
        break;
   ]
}
  • switch(表达式)中表达式的值必须是下述几种类型之一:byte,short,char,int,枚举 (jdk 5.0),String (jdk 7.0);

  • case子句中的值必须是常量,不能是变量名或不确定的表达式值或范围;

  • 同一个switch语句,所有case子句中的常量值互不相同;

  • break语句用来在执行完一个case分支后使程序跳出switch语句块;

    如果没有break,程序会顺序执行到switch结尾;

  • default子句是可选的。同时,位置也是灵活的。放其他位置需要break,否则也会穿透。当没有匹配的case时,执行default语句。

直到遇到break关键字或执行完所有的case及default的执行语句,跳出当前的switch-case结构

如果没有遇到break,则会继续执行当前case之后的其它case中的执行语句。—>case穿透

 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
32
33
34
35
36
37
38
39
40
41
42
43
//switch-case实现:对学生成绩大于60分的,输出“合格”。低于60分的,输出“不合格”
class SwitchCaseTest3 {
	public static void main(String[] args) {
		
		int score = 67;
		//写法1:1-100每个都写

		//写法2:case穿透
		switch(score / 10){
			case 0:
			case 1:
			case 2:
			case 3:
			case 4:
			case 5:
				System.out.println("不及格");
				break;
			case 6:
			case 7:
			case 8:
			case 9:
			case 10:
				System.out.println("及格");
				break;
			default:
				System.out.println("输入的成绩有误");
				break;
		}

		//写法3:
		switch(score / 60){
			case 0:
				System.out.println("不及格");
				break;
			case 1:
				System.out.println("及格");
				break;
			default:
				System.out.println("输入的成绩有误");
				break;
		}
	}
}

case的穿透性

在switch语句中,如果case的后面不写break,将出现穿透现象,也就是一旦匹配成功,不会在判断下一个case的值,直接向后运行,直到遇到break或者整个switch语句结束,执行终止。

判断几月几是第几天

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//键盘上输入2023年的“month”和“day”,要求通过程序输出输入的日期为2023年的第几天。
import java.util.Scanner;

class SwitchCaseTest4 {
	public static void main(String[] args) {
		
		Scanner scan = new Scanner(System.in);
		System.out.println("请输入2023年的month:");
		int month = scan.nextInt();
		System.out.println("请输入2023年的day:");
		int day = scan.nextInt();

		//这里就不针对month和day进行合法性的判断了,以后可以使用正则表达式进行校验。
		int sumDays = 0;//记录总天数

		switch(month){
			case 12:
				sumDays += 30;//这个30是代表11月份的满月天数
			case 11:
				sumDays += 31;//这个31是代表10月份的满月天数
			case 10:
				sumDays += 30;//这个30是代表9月份的满月天数
			case 9:
				sumDays += 31;//这个31是代表8月份的满月天数
			case 8:
				sumDays += 31;//这个31是代表7月份的满月天数
			case 7:
				sumDays += 30;//这个30是代表6月份的满月天数
			case 6:
				sumDays += 31;//这个31是代表5月份的满月天数
			case 5:
				sumDays += 30;//这个30是代表4月份的满月天数
			case 4:
				sumDays += 31;//这个31是代表3月份的满月天数
			case 3:
				sumDays += 28;//这个28是代表2月份的满月天数
			case 2:
				sumDays += 31;//这个31是代表1月份的满月天数
			case 1:
				sumDays += day;//这个day是代表当月的第几天
		}
		
		System.out.println(month + "月" + day + "日是2023年的第" + sumDays + "天");
        //关闭资源
		scan.close();
	}
}

是否是闰年

要判断一年是否是闰年,可以根据以下规则:

  1. 如果年份能被4整除,但不能被100整除,那么它是闰年。
  2. 如果年份能被400整除,那么它也是闰年。
1
2
3
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {  
	isLeapYear = true;  
}  
 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import java.util.Scanner;

public class SwitchCaseTest04 {

    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入year:");
        int year = scanner.nextInt();

        System.out.print("请输入month:");
        int month = scanner.nextInt();

        System.out.print("请输入day:");
        int day = scanner.nextInt();

        //判断这一天是当年的第几天==>从1月1日开始,累加到xx月xx日这一天
        //(1)[1,month-1]个月满月天数
        //(2)单独考虑2月份是否是29天(依据是看year是否是闰年)
        //(3)第month个月的day天

        //声明一个变量days,用来存储总天数
        int sumDays = 0;

        //累加[1,month-1]个月满月天数
        switch (month) {
            case 12:
                //累加的1-11月
                sumDays += 30;//这个30是代表11月份的满月天数
                //这里没有break,继续往下走
            case 11:
                //累加的1-10月
                sumDays += 31;//这个31是代表10月的满月天数
                //这里没有break,继续往下走
            case 10:
                sumDays += 30;//9月
            case 9:
                sumDays += 31;//8月
            case 8:
                sumDays += 31;//7月
            case 7:
                sumDays += 30;//6月
            case 6:
                sumDays += 31;//5月
            case 5:
                sumDays += 30;//4月
            case 4:
                sumDays += 31;//3月
            case 3:
                sumDays += 28;//2月
                //在这里考虑是否可能是29天
                if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
                    sumDays++;//多加1天
                }
            case 2:
                sumDays += 31;//1月
            case 1:
                sumDays += day;//第month月的day天
        }

        //输出结果
        System.out.println(year + "年" + month + "月" + day + "日是这一年的第" + sumDays + "天");

        scanner.close();
    }
}

if-else语句与switch-case语句比较

  • 结论:凡是使用switch-case的结构都可以转换为if-else结构。反之,不成立。

  • 开发经验:如果既可以使用switch-case,又可以使用if-else,建议使用switch-case。因为效率稍高。

  • 细节对比:

    • if-else语句优势
      • if语句的条件是一个布尔类型值,if条件表达式为true则进入分支,可以用于范围的判断,也可以用于等值的判断,使用范围更广
      • switch语句的条件是一个常量值(byte,short,int,char,枚举,String),只能判断某个变量或表达式的结果是否等于某个常量值,使用场景较狭窄
    • switch语句优势
      • 当条件是判断某个变量或表达式是否等于某个固定的常量值时,使用if和switch都可以,习惯上使用switch更多。因为效率稍高。当条件是区间范围的判断时,只能使用if语句。
      • 使用switch可以利用穿透性,同时执行多个分支,而if…else没有穿透性。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
编写程序:从键盘上读入一个学生成绩,存放在变量score中,根据score的值输出其对应的成绩等级:

score>=90           等级:  A
70<=score<90        等级:  B    
60<=score<70        等级:  C
score<60            等级:  D

方式一:使用if-else
方式二:使用switch-case:  score / 10:   0 - 10

编写一个程序,为一个给定的年份找出其对应的中国生肖。中国的生肖基于12年一个周期,每年用一个动物代表:rat、ox、tiger、rabbit、dragon、snake、horse、sheep、monkey、rooster、dog、pig。

提示:2022年:虎   2022 % 12 == 6 -->确定一个,其余可推

循环语句

  • 循环结构分类:

    • for 循环
    • while 循环
    • do-while 循环
  • 循环结构四要素

    • 初始化部分
    • 循环条件部分
    • 循环体部分
    • 迭代部分

for循环

1
2
3
for (初始化部分; 循环条件部分; 迭代部分)
         	循环体部分;

  • for(;;)中的两个;不能多也不能少
  • ①初始化部分可以声明多个变量,但必须是同一个类型,用逗号分隔
  • ②循环条件部分为boolean类型表达式,当值为false时,退出循环
  • ④可以有多个变量更新,用逗号分隔
1
2
3
4
5
6
7
8
9
public class ForTest2 {
	public static void main(String[] args) {
        int num = 1;
        for(System.out.print("a");num < 3;System.out.print("c"),num++){
            System.out.print("b");
        }
    }
}
//abcbc

水仙花数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//水仙花数
public class ForTest4 {
	public static void main(String[] args) {
		//定义统计变量,初始化值是0
		int count = 0;
		
		//获取三位数,用for循环实现
		for(int x = 100; x < 1000; x++) {
			//获取三位数的个位,十位,百位
			int ge = x % 10;
			int shi = x / 10 % 10;//shi=i%100/10
			int bai = x / 100;
			
			//判断这个三位数是否是水仙花数,如果是,统计变量++
			if((ge*ge*ge+shi*shi*shi+bai*bai*bai) == x) {
                System.out.println("水仙花数:" + x);
				count++;
			}
		}
		
		//输出统计结果就可以了
		System.out.println("水仙花数共有"+count+"个");
	}
}

break 跳出当前循环结构

 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
32
33
public class ForTest5 {
    public static void main(String[] args) {
        //需求1:最大公约数
        int m = 12, n = 20;
        //取出两个数中的较小值
        int min = (m < n) ? m : n;

        for (int i = min; i >= 1; i--) {//for(int i = 1;i <= min;i++){

            if (m % i == 0 && n % i == 0) {
                System.out.println("最大公约数是:" + i); //公约数

                break; //跳出当前循环结构
            }
        }


        //需求2:最小公倍数
        //取出两个数中的较大值
        int max = (m > n) ? m : n;

        for (int i = max; i <= m * n; i++) {

            if (i % m == 0 && i % n == 0) {

                System.out.println("最小公倍数是:" + i);//公倍数

                break;
            }
        }

    }
}

while循环

1
2
3
4
5
初始化部分
while(循环条件部分)
    循环体部分;
    迭代部分;
}
  • while(循环条件)中循环条件必须是boolean类型。
  • 注意不要忘记声明④迭代部分。否则,循环将不能结束,变成死循环。
  • for循环和while循环可以相互转换。二者没有性能上的差别。实际开发中,根据具体结构的情况,选择哪个格式更合适、美观。
  • for循环与while循环的区别:初始化条件部分的作用域不同。

do-while循环

1
2
3
4
5
初始化部分;
do{
	循环体部分
	迭代部分
}while(循环条件部分); 
  • 结尾while(循环条件)中循环条件必须是boolean类型
  • do{}while();最后有一个分号
  • do-while结构的循环体语句是至少会执行一次,这个和for和while是不一样的

比较:

  • 从循环次数角度分析
    • do-while循环至少执行一次循环体语句。
    • for和while循环先判断循环条件语句是否成立,然后决定是否执行循环体。
  • 如何选择
    • 遍历有明显的循环次数(范围)的需求,选择for循环
    • 遍历没有明显的循环次数(范围)的需求,选择while循环
    • 如果循环体语句块至少执行一次,可以考虑使用do-while循环
    • 本质上:三种循环之间完全可以互相转换,都能实现循环的功能

“无限"循环

  • 最简单"无限"循环格式:while(true) , for(;;)

  • 开发中,有时并不确定需要循环多少次,需要根据循环体内部某些条件,来控制循环的结束(使用break)。

  • 如果此循环结构不能终止,则构成了死循环!开发中要避免出现死循环。

1
2
3
4
5
6
7
8
public class EndlessFor1 {
    public static void main(String[] args) {
        for (;;){
            System.out.println("我爱你!");
        }
//        System.out.println("end");//永远无法到达的语句,编译报错
    }
}
 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
//从键盘读入个数不确定的整数,并判断读入的正数和负数的个数,输入为0时结束程序。
import java.util.Scanner;

class PositiveNegative {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
        
		int positiveNumber = 0;//统计正数的个数
		int negativeNumber = 0;//统计负数的个数
		for(;;){  //while(true){
			System.out.println("请输入一个整数:(输入为0时结束程序)");
			int num = scanner.nextInt();
			if(num > 0){
				 positiveNumber++;
            }else if(num < 0){
				 negativeNumber++;
        	}else{
                System.out.println("程序结束");
				break; 
            }
         }
		 System.out.println("正数的个数为:"+ positiveNumber);
		 System.out.println("负数的个数为:"+ negativeNumber);  
        
         scanner.close();
	} 
}

嵌套循环

  • 实质上,嵌套循环就是把内层循环当成外层循环的循环体。只有当内层循环的循环条件为false时,才会完全跳出内层循环,才可结束外层的当次循环,开始下一次的外层循环。
  • 设外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次。
  • **技巧:**从二维图形的角度看,外层循环控制行数,内层循环控制列数
  • **开发经验:**实际开发中,我们最多见到的嵌套循环是两层。一般不会出现超过三层的嵌套循环。如果将要出现,一定要停下来重新梳理业务逻辑,重新思考算法的实现,控制在三层以内。否则,可读性会很差。

关键字break和continue

适用范围 在循环结构中使用的作用 相同点
break switch-case循环结构 一旦执行,就结束(或跳出)当前循环结构 此关键字的后面,不能声明语句
continue 循环结构 一旦执行,就结束(或跳出)当次循环结构 此关键字的后面,不能声明语句
 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
class BreakContinueTest1 {
	public static void main(String[] args) {
	
		for(int i = 1;i <= 10;i++){
			
			if(i % 4 == 0){
				//break;//123
				continue;//123567910
				//如下的语句不可能被执行,编译不通过
				//System.out.println("今晚迪丽热巴要约我吃饭");
			}

			System.out.print(i);
		}

		System.out.println("####");

		//嵌套循环中的使用
		for(int i = 1;i <= 4;i++){
		
			for(int j = 1;j <= 10;j++){
				if(j % 4 == 0){
					//break; //结束的是包裹break关键字的最近的一层循环!
					continue;//结束的是包裹break关键字的最近的一层循环的当次!
				}
				System.out.print(j);
			}
			System.out.println();
		}

	}

带标签的使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
break语句用于终止某个语句块的执行
{    ……	 
	break;
	 ……
}

break语句出现在多层嵌套的语句块中时可以通过标签指明要终止的是哪一层语句块 
	label1: {   ……        
	label2:	     {   ……
	label3:			 {   ……
				           break label2;
				           ……
					 }
			     }
			} 
  • continue语句出现在多层嵌套的循环语句体中时,也可以通过标签指明要跳过的是哪一层循环。

  • 标号语句必须紧接在循环的头部。标号语句不能用在非循环语句的前面。

  • 举例:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    class BreakContinueTest2 {
    	public static void main(String[] args) {
          l:for(int i = 1;i <= 4;i++){
    
              for(int j = 1;j <= 10;j++){
                  if(j % 4 == 0){
                      //break l;
                      continue l;
                  }
                  System.out.print(j);
              }
              System.out.println();
          }
    	}
    }
    

找素数

题目:找出100以内所有的素数(质数)?100000以内的呢?

分析:素数(质数):只能被1和它本身整除的自然数。 —> 从2开始,到这个数-1为止,此范围内没有这个数的约数。则此数是一个质数。 比如:2、3、5、7、11、13、17、19、23、…

 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
32
33
34
35
36
37
class PrimeNumberTest {
	public static void main(String[] args) {
		
		boolean isFlag = true; //用于标识i是否被除尽过
		long start = System.currentTimeMillis(); 
        //记录当前时间距离1970-1-1 00:00:00的毫秒数	
		int count = 0;//记录质数的个数

		for(int i = 2;i <= 100000;i++){  //i

			//boolean isFlag = true; //用于标识i是否被除尽过
		
			for(int j = 2;j <= i - 1;j++){
				
				if(i % j == 0){ // 
					isFlag = false;
				}
			
			}

			//判断i是否是质数
			if(isFlag){ //如果isFlag变量没有给修改过值,就意味着i没有被j除尽过。则i是一个质数
				//System.out.println(i);
				count++;
			}

			//重置isFlag
			isFlag = true;
		
		}

		long end = System.currentTimeMillis();
		System.out.println("质数的个数为:" + count);//9592
		System.out.println("执行此程序花费的毫秒数为:" + (end - start)); //6715

	}
}

优化

 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
32
33
34
35
class PrimeNumberTest1 {
	public static void main(String[] args) {
		
		long start = System.currentTimeMillis(); //记录当前时间距离1970-1-1 00:00:00的毫秒数

		int count = 0;//记录质数的个数

		for(int i = 2;i <= 100000;i++){  //i

			boolean isFlag = true; //用于标识i是否被除尽过
			
            // 开根号等号要留着
			for(int j = 2;j <= Math.sqrt(i);j++){ //优化2:将循环条件中的i改为Math.sqrt(i)
				
				if(i % j == 0){ //表明i有约数
					isFlag = false;
					break;//优化1:主要针对非质数起作用--->620
				}
			
			}

			//判断i是否是质数
			if(isFlag){ //如果isFlag变量没有给修改过值,就意味着i没有被j除尽过。则i是一个质数
				//System.out.println(i);
				count++;
			}
		
		}

		long end = System.currentTimeMillis();
		System.out.println("质数的个数为:" + count);
		System.out.println("执行此程序花费的毫秒数为:" + (end - start));//16

	}
}

Scanner:键盘输入功能的实现

  • 如何从键盘获取不同类型(基本数据类型、String类型)的变量:使用Scanner类。

  • 键盘输入代码的四个步骤:

    1. 导包:import java.util.Scanner;

    2. 创建Scanner类型的对象:Scanner scan = new Scanner(System.in);

    3. 调用Scanner类的相关方法(next() / nextXxx()),来获取指定类型的变量

    4. 释放资源:scan.close();

      在 Java 中,当一个对象不再被程序所引用时,Java 的垃圾回收机制会负责回收该对象所占用的内存空间。然而,对于一些底层资源(比如文件句柄、网络连接等),Java 的垃圾回收机制无法直接回收,这就需要程序员手动关闭这些资源。

      如果不及时关闭 Scanner 对象,它所占用的系统资源将无法被及时释放,可能导致资源泄漏,最终会影响系统的稳定性和性能。因此,通过调用 scan.close()方法,可以及时释放 Scanner 对象占用的系统资源,从而防止内存泄漏问题的发生。

  • 注意:需要根据相应的方法,来输入指定类型的值。如果输入的数据类型与要求的类型不匹配时,会报异常 导致程序终止。

 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
32
33
34
35
36
37
38
39
40
41
42
43
//① 导包
import java.util.Scanner;

public class ScannerTest1 {

    public static void main(String[] args) {
        //② 创建Scanner的对象
        //Scanner是一个引用数据类型,它的全名称是java.util.Scanner
        //scanner就是一个引用数据类型的变量了,赋给它的值是一个对象
        //new Scanner(System.in)是一个new表达式,该表达式的结果是一个对象
        //引用数据类型  变量 = 对象;
        //这个等式的意思可以理解为用一个引用数据类型的变量代表一个对象,所以这个变量的名称又称为对象名
        //我们也把scanner变量叫做scanner对象
        Scanner scanner = new Scanner(System.in);//System.in默认代表键盘输入
        
        //③根据提示,调用Scanner的方法,获取不同类型的变量
        System.out.println("欢迎光临你好我好交友网站!");
        System.out.print("请输入你的网名:");
        String name = scanner.next();

        System.out.print("请输入你的年龄:");
        int age = scanner.nextInt();

        System.out.print("请输入你的体重:");
        double weight = scanner.nextDouble();

        System.out.print("你是否单身(true/false):");
        //System.out.print("你是否单身(true\\false):");
        //一个\会报错,表示转义字符两个就可以正常输出
        boolean isSingle = scanner.nextBoolean();

        System.out.print("请输入你的性别:");
        ////先按照字符串接收,然后再取字符串的第一个字符(下标为0)
        char gender = scanner.next().charAt(0);

        System.out.println("你的基本情况如下:");
        System.out.println("网名:" + name + "\n年龄:" + age + "\n体重:" + weight + 
                           "\n单身:" + isSingle + "\n性别:" + gender);
        
        //④ 关闭资源
        scanner.close();
    }
}

中文报错,编码改为ANSI

字符串相等:

"是".equals(str) 判读字符串的内容

str== “是” —不行,判断两个字符串是不是同一个

随机数

Math.random()方法

Math类的random()的调用,会返回一个[0,1)范围的一个double型值

1
2
3
4
5
6
7
8
9
double randomValue = Math.random();

Math.random() * 100  --->  [0,100)
(int)(Math.random() * 100)	---> [0,99]
(int)(Math.random() * 101)	---> [0,100]                        
(int)(Math.random() * 100) + 5  ----> [5,104]

//先取[0,b-a],然后+a
(int)(Math.random() * (b - a + 1)) + a	----> [a,b]

java.util.Random类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import java.util.Random;  
  
// 创建 Random 对象  
Random random = new Random();  
  
// 生成随机整数  
int randomNumber = random.nextInt(100); // 生成 0 到 99 之间的随机整数  
  
// 生成随机双精度浮点数  
double randomDouble = random.nextDouble(); // 生成 0.0 到 1.0 之间的随机双精度浮点数  

小项目:记账软件

需求:该软件能够记录家庭的收入、支出,并能够打印收支明细表

•假设家庭起始的生活基本金为10000元。

•每次登记收入(菜单2)后,收入的金额应累加到基本金上,并记录本次收入明细,以便后续的查询。

•每次登记支出(菜单3)后,支出的金额应从基本金中扣除,并记录本次支出明细,以便后续的查询。

•查询收支明细( 菜单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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import java.util.*;
/**
Utility工具类:
将不同的功能封装为方法,就是可以直接通过调用方法使用它的功能,而无需考虑具体的功能实现细节。
*/
public class Utility {
    // 声明了一个静态的、私有的Scanner类型的变量`scanner`,用于从命令行获取用户输入
    private static Scanner scanner = new Scanner(System.in);
    /**
	用于界面菜单的选择。该方法读取键盘,如果用户键入’1’-’4’中的任意字符,则方法返回。返回值为用户键入字符。
	*/
	public static char readMenuSelection() {
        char c;
        for (; ; ) {
            String str = readKeyBoard(1); 
            c = str.charAt(0);//获取字符串中索引位置为0的字符
            if (c != '1' && c != '2' && c != '3' && c != '4') {
                System.out.print("选择错误,请重新输入:");
            } else break;
        }
        return c;
    }
	/**
	用于收入和支出金额的输入。该方法从键盘读取一个不超过4位长度的整数,并将其作为方法的返回值。
	*/
    public static int readNumber() {
        int n;
        for (; ; ) {
            String str = readKeyBoard(4);
            try {
                n = Integer.parseInt(str);
                break;
            } catch (NumberFormatException e) {
                System.out.print("数字输入错误,请重新输入:");
            }
        }
        return n;
    }
	/**
	用于收入和支出说明的输入。该方法从键盘读取一个不超过8位长度的字符串,并将其作为方法的返回值。
	*/
    public static String readString() {
        String str = readKeyBoard(8);
        return str;
    }
	
	/**
	用于确认选择的输入。该方法从键盘读取‘Y’或’N’,并将其作为方法的返回值。
	*/
    public static char readConfirmSelection() {
        char c;
        for (; ; ) {
            String str = readKeyBoard(1).toUpperCase();
            c = str.charAt(0);
            if (c == 'Y' || c == 'N') {
                break;
            } else {
                System.out.print("选择错误,请重新输入:");
            }
        }
        return c;
    }
	
	
    private static String readKeyBoard(int limit) {
        String line = "";
		
        // 使用 hasNext() 检查是否还有下一个 token 
        while (scanner.hasNext()) {
            line = scanner.nextLine();
            if (line.length() < 1 || line.length() > limit) {
                System.out.print("输入长度(不大于" + limit + ")错误,请重新输入:");
                continue;
            }
            break;
        }

        return line;
    }
}

scanner.hasNext() 是一个用于检查输入流中是否还有下一个 token(标记)的方法。在 Java 中,Scanner 类可以用来从输入流中获取用户输入。hasNext() 方法返回一个布尔值,指示是否还有下一个 token 可用。

while (scanner.hasNext()) {
String input = scanner.next(); // 读取下一个 token
System.out.println(“You entered: " + input);
}

Enter a string: qqq You entered: qqq aaa You entered: aaa ccc You entered: ccc

BLOG

c = str.charAt(0);//获取字符串中索引位置为0的字符

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public class FamilyAccount {

	public static void main(String[] args) {

		boolean isFlag = true;
		String details = "收支\t账户金额\t\t收支金额\t说    明\n";
		double balance = 10000;//初始账户 金额

		do {
			System.out
					.println("\n-----------------谷粒收支记账软件-----------------\n");
			System.out.println("                   1 收支明细");
			System.out.println("                   2 登记收入");
			System.out.println("                   3 登记支出");
			System.out.println("                   4 退    出\n");
			System.out.print("                   请选择(1-4):");

			// 要求用户输入1-4选择
			char menu = Utility.readMenuSelection();
			switch (menu) {
			case '1':
//				System.out.println("显示详情");
				System.out.println("-----------------当前收支明细记录-----------------");
				System.out.println(details);
				
				System.out.println();
				System.out.println("---------------------------------------------");
				
				break;
			case '2':
//				System.out.println("收入情况");
				System.out.print("本次收入金额:");
				int money = Utility.readNumber();//从键盘获取收入的金额
				
				System.out.print("本次收入说明:");
				String desc = Utility.readString();//从键盘获取收入的说明
				
				//根据获取的金额和收入说明修改用户的信息
				balance += money;
				
				details += ("收入\t" + balance + "\t\t" + money + "\t" +desc + "\n");
				
				System.out.println("---------------------登记完成-------------------");
				break;
			case '3':
//				System.out.println("支出情况");
				System.out.print("本次支出金额:");
				int money1 = Utility.readNumber();//从键盘获取支出的金额
				
				System.out.print("本次支出说明:");
				String desc1 = Utility.readString();//从键盘获取支出的说明
				
				//根据获取的金额和支出说明修改用户的信息
				if(balance >= money1){
					balance -= money1;
					details += ("支出\t" + balance + "\t\t" + money1 + "\t" +desc1 + "\n");
					System.out.println("---------------------登记完成-------------------");
				}else{
					System.out.println("余额不足,支出失败");
				}
				
				break;
			case '4':
				System.out.print("确认是否退出(Y/N):");
				char exit = Utility.readConfirmSelection();
				if(exit == 'Y'){
					isFlag = false;
				}
				
				break;

			}

		} while (isFlag);

	}

}

总结:

switch-case

  • 在特殊的场景下,分支结构可以考虑使用switch-case

    • 指定的数据类型:byte \ short \ char \ int ; 枚举类(jdk5.0)\ String (jdk7.0)
    • 可以考虑的常量值有限且取值情况不多。
  • 特别之处:case穿透。

  • 在能使用switch-case的情况下,推荐使用switch-case,因为比if-else效率稍高

Scanner类中提供了获取byte \ short \ int \ long \float \double \boolean \ String类型变量的方法。— title: “Java流程控制” date: 2023-12-01T16:54:57+08:00 categories: [“JAVA”] draft: false


程序设计中规定的三种流程结构,即:

  • 顺序结构
    • 程序从上到下逐行地执行,中间没有任何判断和跳转。
  • 分支结构
    • 根据条件,选择性地执行某段代码。
    • if…elseswitch-case两种分支语句。
  • 循环结构
    • 根据循环条件,重复性的执行某段代码。
    • forwhiledo-while三种循环语句。
    • 补充:JDK5.0 提供了foreach循环,方便的遍历集合、数组元素。(第12章集合中讲解)

分支语句

if-else条件判断结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
if(条件表达式)
  	语句块;


if(条件表达式) { 
  	语句块1;
}else {
  	语句块2;
}

if (条件表达式1) {
  	语句块1;
} else if (条件表达式2) {
  	语句块2;
}
...
}else if (条件表达式n) {
 	语句块n;
} else {
  	语句块n+1;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class IfElseExer4 {
    public static void main(String[] args) {
        System.out.print("小明的期末Java成绩是:[0,100]");
        int score = 89;

        if (score < 0 || score > 100) {
            System.out.println("你的成绩是错误的");
        } else if (score >= 90) {
            System.out.println("你的成绩属于优秀");
        } else if (score >= 80) {
            System.out.println("你的成绩属于好");
        } else if (score >= 70) {
            System.out.println("你的成绩属于良");
        } else if (score >= 60) {
            System.out.println("你的成绩属于及格");
        } else {
            System.out.println("你的成绩属于不及格");
        }

    }
}

当条件表达式之间是“互斥”关系时(即彼此没有交集),条件判断语句及执行语句间顺序无所谓。

当条件表达式之间是“包含”关系时,“小上大下 / 子上父下”,否则范围小的条件表达式将不可能被执行。

if…else嵌套

 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
class IfElseTest4 {
	public static void main(String[] args) {
		
			//声明num1,num2,num3三个变量并赋值
			int num1 = 23,num2 = 32,num3 = 12;

			if(num1 >= num2){
				
				if(num3 >= num1)
					System.out.println(num2 + "-" + num1 + "-" + num3);
				else if(num3 <= num2)
					System.out.println(num3 + "-" + num2 + "-" + num1);
				else
					System.out.println(num2 + "-" + num3 + "-" + num1);
			}else{ //num1 < num2
				
				if(num3 >= num2){
					System.out.println(num1 + "-" + num2 + "-" + num3);
				}else if(num3 <= num1){
					System.out.println(num3 + "-" + num1 + "-" + num2);
				}else{
					System.out.println(num1 + "-" + num3 + "-" + num2);
				}
			}
	}
}
  • 语句块只有一条执行语句时,一对{}可以省略,但建议保留
  • 当if-else结构是“多选一”时,最后的else是可选的,根据需要可以省略
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//1)对下列代码,若有输出,指出输出结果。
int x = 4;
int y = 1;
if (x > 2) {
       if (y > 2) 
            System.out.println(x + y);
    		//if没有括号只包含一行,下一行在if结构外。
       		System.out.println("hello");
    	//System.out.println("hello");//相当于在这
} else
       System.out.println("x is " + x);
//hello
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
boolean b = true;
//if(b=false) //赋值b为false,不满足 c
if(b == false)//判断b为false b	 //建议:if(!b)
	System.out.println("a");
else if(b)
	System.out.println("b");
else if(!b)
	System.out.println("c");
else
	System.out.println("d");

switch-case选择结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
switch(表达式){
    case 常量值1:
        语句块1;
        //break;
    case 常量值2:
        语句块2;
        //break; 
    // ...
   [default:
        语句块n+1;
        break;
   ]
}
  • switch(表达式)中表达式的值必须是下述几种类型之一:byte,short,char,int,枚举 (jdk 5.0),String (jdk 7.0);

  • case子句中的值必须是常量,不能是变量名或不确定的表达式值或范围;

  • 同一个switch语句,所有case子句中的常量值互不相同;

  • break语句用来在执行完一个case分支后使程序跳出switch语句块;

    如果没有break,程序会顺序执行到switch结尾;

  • default子句是可选的。同时,位置也是灵活的。放其他位置需要break,否则也会穿透。当没有匹配的case时,执行default语句。

直到遇到break关键字或执行完所有的case及default的执行语句,跳出当前的switch-case结构

如果没有遇到break,则会继续执行当前case之后的其它case中的执行语句。—>case穿透

 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
32
33
34
35
36
37
38
39
40
41
42
43
//switch-case实现:对学生成绩大于60分的,输出“合格”。低于60分的,输出“不合格”
class SwitchCaseTest3 {
	public static void main(String[] args) {
		
		int score = 67;
		//写法1:1-100每个都写

		//写法2:case穿透
		switch(score / 10){
			case 0:
			case 1:
			case 2:
			case 3:
			case 4:
			case 5:
				System.out.println("不及格");
				break;
			case 6:
			case 7:
			case 8:
			case 9:
			case 10:
				System.out.println("及格");
				break;
			default:
				System.out.println("输入的成绩有误");
				break;
		}

		//写法3:
		switch(score / 60){
			case 0:
				System.out.println("不及格");
				break;
			case 1:
				System.out.println("及格");
				break;
			default:
				System.out.println("输入的成绩有误");
				break;
		}
	}
}

case的穿透性

在switch语句中,如果case的后面不写break,将出现穿透现象,也就是一旦匹配成功,不会在判断下一个case的值,直接向后运行,直到遇到break或者整个switch语句结束,执行终止。

判断几月几是第几天

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//键盘上输入2023年的“month”和“day”,要求通过程序输出输入的日期为2023年的第几天。
import java.util.Scanner;

class SwitchCaseTest4 {
	public static void main(String[] args) {
		
		Scanner scan = new Scanner(System.in);
		System.out.println("请输入2023年的month:");
		int month = scan.nextInt();
		System.out.println("请输入2023年的day:");
		int day = scan.nextInt();

		//这里就不针对month和day进行合法性的判断了,以后可以使用正则表达式进行校验。
		int sumDays = 0;//记录总天数

		switch(month){
			case 12:
				sumDays += 30;//这个30是代表11月份的满月天数
			case 11:
				sumDays += 31;//这个31是代表10月份的满月天数
			case 10:
				sumDays += 30;//这个30是代表9月份的满月天数
			case 9:
				sumDays += 31;//这个31是代表8月份的满月天数
			case 8:
				sumDays += 31;//这个31是代表7月份的满月天数
			case 7:
				sumDays += 30;//这个30是代表6月份的满月天数
			case 6:
				sumDays += 31;//这个31是代表5月份的满月天数
			case 5:
				sumDays += 30;//这个30是代表4月份的满月天数
			case 4:
				sumDays += 31;//这个31是代表3月份的满月天数
			case 3:
				sumDays += 28;//这个28是代表2月份的满月天数
			case 2:
				sumDays += 31;//这个31是代表1月份的满月天数
			case 1:
				sumDays += day;//这个day是代表当月的第几天
		}
		
		System.out.println(month + "月" + day + "日是2023年的第" + sumDays + "天");
        //关闭资源
		scan.close();
	}
}

是否是闰年

要判断一年是否是闰年,可以根据以下规则:

  1. 如果年份能被4整除,但不能被100整除,那么它是闰年。
  2. 如果年份能被400整除,那么它也是闰年。
1
2
3
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {  
	isLeapYear = true;  
}  
 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import java.util.Scanner;

public class SwitchCaseTest04 {

    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入year:");
        int year = scanner.nextInt();

        System.out.print("请输入month:");
        int month = scanner.nextInt();

        System.out.print("请输入day:");
        int day = scanner.nextInt();

        //判断这一天是当年的第几天==>从1月1日开始,累加到xx月xx日这一天
        //(1)[1,month-1]个月满月天数
        //(2)单独考虑2月份是否是29天(依据是看year是否是闰年)
        //(3)第month个月的day天

        //声明一个变量days,用来存储总天数
        int sumDays = 0;

        //累加[1,month-1]个月满月天数
        switch (month) {
            case 12:
                //累加的1-11月
                sumDays += 30;//这个30是代表11月份的满月天数
                //这里没有break,继续往下走
            case 11:
                //累加的1-10月
                sumDays += 31;//这个31是代表10月的满月天数
                //这里没有break,继续往下走
            case 10:
                sumDays += 30;//9月
            case 9:
                sumDays += 31;//8月
            case 8:
                sumDays += 31;//7月
            case 7:
                sumDays += 30;//6月
            case 6:
                sumDays += 31;//5月
            case 5:
                sumDays += 30;//4月
            case 4:
                sumDays += 31;//3月
            case 3:
                sumDays += 28;//2月
                //在这里考虑是否可能是29天
                if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
                    sumDays++;//多加1天
                }
            case 2:
                sumDays += 31;//1月
            case 1:
                sumDays += day;//第month月的day天
        }

        //输出结果
        System.out.println(year + "年" + month + "月" + day + "日是这一年的第" + sumDays + "天");

        scanner.close();
    }
}

if-else语句与switch-case语句比较

  • 结论:凡是使用switch-case的结构都可以转换为if-else结构。反之,不成立。

  • 开发经验:如果既可以使用switch-case,又可以使用if-else,建议使用switch-case。因为效率稍高。

  • 细节对比:

    • if-else语句优势
      • if语句的条件是一个布尔类型值,if条件表达式为true则进入分支,可以用于范围的判断,也可以用于等值的判断,使用范围更广
      • switch语句的条件是一个常量值(byte,short,int,char,枚举,String),只能判断某个变量或表达式的结果是否等于某个常量值,使用场景较狭窄
    • switch语句优势
      • 当条件是判断某个变量或表达式是否等于某个固定的常量值时,使用if和switch都可以,习惯上使用switch更多。因为效率稍高。当条件是区间范围的判断时,只能使用if语句。
      • 使用switch可以利用穿透性,同时执行多个分支,而if…else没有穿透性。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
编写程序:从键盘上读入一个学生成绩,存放在变量score中,根据score的值输出其对应的成绩等级:

score>=90           等级:  A
70<=score<90        等级:  B    
60<=score<70        等级:  C
score<60            等级:  D

方式一:使用if-else
方式二:使用switch-case:  score / 10:   0 - 10

编写一个程序,为一个给定的年份找出其对应的中国生肖。中国的生肖基于12年一个周期,每年用一个动物代表:rat、ox、tiger、rabbit、dragon、snake、horse、sheep、monkey、rooster、dog、pig。

提示:2022年:虎   2022 % 12 == 6 -->确定一个,其余可推

循环语句

  • 循环结构分类:

    • for 循环
    • while 循环
    • do-while 循环
  • 循环结构四要素

    • 初始化部分
    • 循环条件部分
    • 循环体部分
    • 迭代部分

for循环

1
2
3
for (初始化部分; 循环条件部分; 迭代部分)
         	循环体部分;

  • for(;;)中的两个;不能多也不能少
  • ①初始化部分可以声明多个变量,但必须是同一个类型,用逗号分隔
  • ②循环条件部分为boolean类型表达式,当值为false时,退出循环
  • ④可以有多个变量更新,用逗号分隔
1
2
3
4
5
6
7
8
9
public class ForTest2 {
	public static void main(String[] args) {
        int num = 1;
        for(System.out.print("a");num < 3;System.out.print("c"),num++){
            System.out.print("b");
        }
    }
}
//abcbc

水仙花数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//水仙花数
public class ForTest4 {
	public static void main(String[] args) {
		//定义统计变量,初始化值是0
		int count = 0;
		
		//获取三位数,用for循环实现
		for(int x = 100; x < 1000; x++) {
			//获取三位数的个位,十位,百位
			int ge = x % 10;
			int shi = x / 10 % 10;//shi=i%100/10
			int bai = x / 100;
			
			//判断这个三位数是否是水仙花数,如果是,统计变量++
			if((ge*ge*ge+shi*shi*shi+bai*bai*bai) == x) {
                System.out.println("水仙花数:" + x);
				count++;
			}
		}
		
		//输出统计结果就可以了
		System.out.println("水仙花数共有"+count+"个");
	}
}

break 跳出当前循环结构

 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
32
33
public class ForTest5 {
    public static void main(String[] args) {
        //需求1:最大公约数
        int m = 12, n = 20;
        //取出两个数中的较小值
        int min = (m < n) ? m : n;

        for (int i = min; i >= 1; i--) {//for(int i = 1;i <= min;i++){

            if (m % i == 0 && n % i == 0) {
                System.out.println("最大公约数是:" + i); //公约数

                break; //跳出当前循环结构
            }
        }


        //需求2:最小公倍数
        //取出两个数中的较大值
        int max = (m > n) ? m : n;

        for (int i = max; i <= m * n; i++) {

            if (i % m == 0 && i % n == 0) {

                System.out.println("最小公倍数是:" + i);//公倍数

                break;
            }
        }

    }
}

while循环

1
2
3
4
5
初始化部分
while(循环条件部分)
    循环体部分;
    迭代部分;
}
  • while(循环条件)中循环条件必须是boolean类型。
  • 注意不要忘记声明④迭代部分。否则,循环将不能结束,变成死循环。
  • for循环和while循环可以相互转换。二者没有性能上的差别。实际开发中,根据具体结构的情况,选择哪个格式更合适、美观。
  • for循环与while循环的区别:初始化条件部分的作用域不同。

do-while循环

1
2
3
4
5
初始化部分;
do{
	循环体部分
	迭代部分
}while(循环条件部分); 
  • 结尾while(循环条件)中循环条件必须是boolean类型
  • do{}while();最后有一个分号
  • do-while结构的循环体语句是至少会执行一次,这个和for和while是不一样的

比较:

  • 从循环次数角度分析
    • do-while循环至少执行一次循环体语句。
    • for和while循环先判断循环条件语句是否成立,然后决定是否执行循环体。
  • 如何选择
    • 遍历有明显的循环次数(范围)的需求,选择for循环
    • 遍历没有明显的循环次数(范围)的需求,选择while循环
    • 如果循环体语句块至少执行一次,可以考虑使用do-while循环
    • 本质上:三种循环之间完全可以互相转换,都能实现循环的功能

“无限"循环

  • 最简单"无限"循环格式:while(true) , for(;;)

  • 开发中,有时并不确定需要循环多少次,需要根据循环体内部某些条件,来控制循环的结束(使用break)。

  • 如果此循环结构不能终止,则构成了死循环!开发中要避免出现死循环。

1
2
3
4
5
6
7
8
public class EndlessFor1 {
    public static void main(String[] args) {
        for (;;){
            System.out.println("我爱你!");
        }
//        System.out.println("end");//永远无法到达的语句,编译报错
    }
}
 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
//从键盘读入个数不确定的整数,并判断读入的正数和负数的个数,输入为0时结束程序。
import java.util.Scanner;

class PositiveNegative {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
        
		int positiveNumber = 0;//统计正数的个数
		int negativeNumber = 0;//统计负数的个数
		for(;;){  //while(true){
			System.out.println("请输入一个整数:(输入为0时结束程序)");
			int num = scanner.nextInt();
			if(num > 0){
				 positiveNumber++;
            }else if(num < 0){
				 negativeNumber++;
        	}else{
                System.out.println("程序结束");
				break; 
            }
         }
		 System.out.println("正数的个数为:"+ positiveNumber);
		 System.out.println("负数的个数为:"+ negativeNumber);  
        
         scanner.close();
	} 
}

嵌套循环

  • 实质上,嵌套循环就是把内层循环当成外层循环的循环体。只有当内层循环的循环条件为false时,才会完全跳出内层循环,才可结束外层的当次循环,开始下一次的外层循环。
  • 设外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次。
  • **技巧:**从二维图形的角度看,外层循环控制行数,内层循环控制列数
  • **开发经验:**实际开发中,我们最多见到的嵌套循环是两层。一般不会出现超过三层的嵌套循环。如果将要出现,一定要停下来重新梳理业务逻辑,重新思考算法的实现,控制在三层以内。否则,可读性会很差。

关键字break和continue

适用范围 在循环结构中使用的作用 相同点
break switch-case循环结构 一旦执行,就结束(或跳出)当前循环结构 此关键字的后面,不能声明语句
continue 循环结构 一旦执行,就结束(或跳出)当次循环结构 此关键字的后面,不能声明语句
 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
class BreakContinueTest1 {
	public static void main(String[] args) {
	
		for(int i = 1;i <= 10;i++){
			
			if(i % 4 == 0){
				//break;//123
				continue;//123567910
				//如下的语句不可能被执行,编译不通过
				//System.out.println("今晚迪丽热巴要约我吃饭");
			}

			System.out.print(i);
		}

		System.out.println("####");

		//嵌套循环中的使用
		for(int i = 1;i <= 4;i++){
		
			for(int j = 1;j <= 10;j++){
				if(j % 4 == 0){
					//break; //结束的是包裹break关键字的最近的一层循环!
					continue;//结束的是包裹break关键字的最近的一层循环的当次!
				}
				System.out.print(j);
			}
			System.out.println();
		}

	}

带标签的使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
break语句用于终止某个语句块的执行
{    ……	 
	break;
	 ……
}

break语句出现在多层嵌套的语句块中时可以通过标签指明要终止的是哪一层语句块 
	label1: {   ……        
	label2:	     {   ……
	label3:			 {   ……
				           break label2;
				           ……
					 }
			     }
			} 
  • continue语句出现在多层嵌套的循环语句体中时,也可以通过标签指明要跳过的是哪一层循环。

  • 标号语句必须紧接在循环的头部。标号语句不能用在非循环语句的前面。

  • 举例:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    class BreakContinueTest2 {
    	public static void main(String[] args) {
          l:for(int i = 1;i <= 4;i++){
    
              for(int j = 1;j <= 10;j++){
                  if(j % 4 == 0){
                      //break l;
                      continue l;
                  }
                  System.out.print(j);
              }
              System.out.println();
          }
    	}
    }
    

找素数

题目:找出100以内所有的素数(质数)?100000以内的呢?

分析:素数(质数):只能被1和它本身整除的自然数。 —> 从2开始,到这个数-1为止,此范围内没有这个数的约数。则此数是一个质数。 比如:2、3、5、7、11、13、17、19、23、…

 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
32
33
34
35
36
37
class PrimeNumberTest {
	public static void main(String[] args) {
		
		boolean isFlag = true; //用于标识i是否被除尽过
		long start = System.currentTimeMillis(); 
        //记录当前时间距离1970-1-1 00:00:00的毫秒数	
		int count = 0;//记录质数的个数

		for(int i = 2;i <= 100000;i++){  //i

			//boolean isFlag = true; //用于标识i是否被除尽过
		
			for(int j = 2;j <= i - 1;j++){
				
				if(i % j == 0){ // 
					isFlag = false;
				}
			
			}

			//判断i是否是质数
			if(isFlag){ //如果isFlag变量没有给修改过值,就意味着i没有被j除尽过。则i是一个质数
				//System.out.println(i);
				count++;
			}

			//重置isFlag
			isFlag = true;
		
		}

		long end = System.currentTimeMillis();
		System.out.println("质数的个数为:" + count);//9592
		System.out.println("执行此程序花费的毫秒数为:" + (end - start)); //6715

	}
}

优化

 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
32
33
34
35
class PrimeNumberTest1 {
	public static void main(String[] args) {
		
		long start = System.currentTimeMillis(); //记录当前时间距离1970-1-1 00:00:00的毫秒数

		int count = 0;//记录质数的个数

		for(int i = 2;i <= 100000;i++){  //i

			boolean isFlag = true; //用于标识i是否被除尽过
			
            // 开根号等号要留着
			for(int j = 2;j <= Math.sqrt(i);j++){ //优化2:将循环条件中的i改为Math.sqrt(i)
				
				if(i % j == 0){ //表明i有约数
					isFlag = false;
					break;//优化1:主要针对非质数起作用--->620
				}
			
			}

			//判断i是否是质数
			if(isFlag){ //如果isFlag变量没有给修改过值,就意味着i没有被j除尽过。则i是一个质数
				//System.out.println(i);
				count++;
			}
		
		}

		long end = System.currentTimeMillis();
		System.out.println("质数的个数为:" + count);
		System.out.println("执行此程序花费的毫秒数为:" + (end - start));//16

	}
}

Scanner:键盘输入功能的实现

  • 如何从键盘获取不同类型(基本数据类型、String类型)的变量:使用Scanner类。

  • 键盘输入代码的四个步骤:

    1. 导包:import java.util.Scanner;

    2. 创建Scanner类型的对象:Scanner scan = new Scanner(System.in);

    3. 调用Scanner类的相关方法(next() / nextXxx()),来获取指定类型的变量

    4. 释放资源:scan.close();

      在 Java 中,当一个对象不再被程序所引用时,Java 的垃圾回收机制会负责回收该对象所占用的内存空间。然而,对于一些底层资源(比如文件句柄、网络连接等),Java 的垃圾回收机制无法直接回收,这就需要程序员手动关闭这些资源。

      如果不及时关闭 Scanner 对象,它所占用的系统资源将无法被及时释放,可能导致资源泄漏,最终会影响系统的稳定性和性能。因此,通过调用 scan.close()方法,可以及时释放 Scanner 对象占用的系统资源,从而防止内存泄漏问题的发生。

  • 注意:需要根据相应的方法,来输入指定类型的值。如果输入的数据类型与要求的类型不匹配时,会报异常 导致程序终止。

 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
32
33
34
35
36
37
38
39
40
41
42
43
//① 导包
import java.util.Scanner;

public class ScannerTest1 {

    public static void main(String[] args) {
        //② 创建Scanner的对象
        //Scanner是一个引用数据类型,它的全名称是java.util.Scanner
        //scanner就是一个引用数据类型的变量了,赋给它的值是一个对象
        //new Scanner(System.in)是一个new表达式,该表达式的结果是一个对象
        //引用数据类型  变量 = 对象;
        //这个等式的意思可以理解为用一个引用数据类型的变量代表一个对象,所以这个变量的名称又称为对象名
        //我们也把scanner变量叫做scanner对象
        Scanner scanner = new Scanner(System.in);//System.in默认代表键盘输入
        
        //③根据提示,调用Scanner的方法,获取不同类型的变量
        System.out.println("欢迎光临你好我好交友网站!");
        System.out.print("请输入你的网名:");
        String name = scanner.next();

        System.out.print("请输入你的年龄:");
        int age = scanner.nextInt();

        System.out.print("请输入你的体重:");
        double weight = scanner.nextDouble();

        System.out.print("你是否单身(true/false):");
        //System.out.print("你是否单身(true\\false):");
        //一个\会报错,表示转义字符两个就可以正常输出
        boolean isSingle = scanner.nextBoolean();

        System.out.print("请输入你的性别:");
        ////先按照字符串接收,然后再取字符串的第一个字符(下标为0)
        char gender = scanner.next().charAt(0);

        System.out.println("你的基本情况如下:");
        System.out.println("网名:" + name + "\n年龄:" + age + "\n体重:" + weight + 
                           "\n单身:" + isSingle + "\n性别:" + gender);
        
        //④ 关闭资源
        scanner.close();
    }
}

中文报错,编码改为ANSI

字符串相等:

"是".equals(str) 判读字符串的内容

str== “是” —不行,判断两个字符串是不是同一个

随机数

Math.random()方法

Math类的random()的调用,会返回一个[0,1)范围的一个double型值

1
2
3
4
5
6
7
8
9
double randomValue = Math.random();

Math.random() * 100  --->  [0,100)
(int)(Math.random() * 100)	---> [0,99]
(int)(Math.random() * 101)	---> [0,100]                        
(int)(Math.random() * 100) + 5  ----> [5,104]

//先取[0,b-a],然后+a
(int)(Math.random() * (b - a + 1)) + a	----> [a,b]

java.util.Random类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import java.util.Random;  
  
// 创建 Random 对象  
Random random = new Random();  
  
// 生成随机整数  
int randomNumber = random.nextInt(100); // 生成 0 到 99 之间的随机整数  
  
// 生成随机双精度浮点数  
double randomDouble = random.nextDouble(); // 生成 0.0 到 1.0 之间的随机双精度浮点数  

小项目:记账软件

需求:该软件能够记录家庭的收入、支出,并能够打印收支明细表

•假设家庭起始的生活基本金为10000元。

•每次登记收入(菜单2)后,收入的金额应累加到基本金上,并记录本次收入明细,以便后续的查询。

•每次登记支出(菜单3)后,支出的金额应从基本金中扣除,并记录本次支出明细,以便后续的查询。

•查询收支明细( 菜单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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import java.util.*;
/**
Utility工具类:
将不同的功能封装为方法,就是可以直接通过调用方法使用它的功能,而无需考虑具体的功能实现细节。
*/
public class Utility {
    // 声明了一个静态的、私有的Scanner类型的变量`scanner`,用于从命令行获取用户输入
    private static Scanner scanner = new Scanner(System.in);
    /**
	用于界面菜单的选择。该方法读取键盘,如果用户键入’1’-’4’中的任意字符,则方法返回。返回值为用户键入字符。
	*/
	public static char readMenuSelection() {
        char c;
        for (; ; ) {
            String str = readKeyBoard(1); 
            c = str.charAt(0);//获取字符串中索引位置为0的字符
            if (c != '1' && c != '2' && c != '3' && c != '4') {
                System.out.print("选择错误,请重新输入:");
            } else break;
        }
        return c;
    }
	/**
	用于收入和支出金额的输入。该方法从键盘读取一个不超过4位长度的整数,并将其作为方法的返回值。
	*/
    public static int readNumber() {
        int n;
        for (; ; ) {
            String str = readKeyBoard(4);
            try {
                n = Integer.parseInt(str);
                break;
            } catch (NumberFormatException e) {
                System.out.print("数字输入错误,请重新输入:");
            }
        }
        return n;
    }
	/**
	用于收入和支出说明的输入。该方法从键盘读取一个不超过8位长度的字符串,并将其作为方法的返回值。
	*/
    public static String readString() {
        String str = readKeyBoard(8);
        return str;
    }
	
	/**
	用于确认选择的输入。该方法从键盘读取‘Y’或’N’,并将其作为方法的返回值。
	*/
    public static char readConfirmSelection() {
        char c;
        for (; ; ) {
            String str = readKeyBoard(1).toUpperCase();
            c = str.charAt(0);
            if (c == 'Y' || c == 'N') {
                break;
            } else {
                System.out.print("选择错误,请重新输入:");
            }
        }
        return c;
    }
	
	
    private static String readKeyBoard(int limit) {
        String line = "";
		
        // 使用 hasNext() 检查是否还有下一个 token 
        while (scanner.hasNext()) {
            line = scanner.nextLine();
            if (line.length() < 1 || line.length() > limit) {
                System.out.print("输入长度(不大于" + limit + ")错误,请重新输入:");
                continue;
            }
            break;
        }

        return line;
    }
}

scanner.hasNext() 是一个用于检查输入流中是否还有下一个 token(标记)的方法。在 Java 中,Scanner 类可以用来从输入流中获取用户输入。hasNext() 方法返回一个布尔值,指示是否还有下一个 token 可用。

while (scanner.hasNext()) {
String input = scanner.next(); // 读取下一个 token
System.out.println(“You entered: " + input);
}

Enter a string: qqq You entered: qqq aaa You entered: aaa ccc You entered: ccc

BLOG

c = str.charAt(0);//获取字符串中索引位置为0的字符

 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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
public class FamilyAccount {

	public static void main(String[] args) {

		boolean isFlag = true;
		String details = "收支\t账户金额\t\t收支金额\t说    明\n";
		double balance = 10000;//初始账户 金额

		do {
			System.out
					.println("\n-----------------谷粒收支记账软件-----------------\n");
			System.out.println("                   1 收支明细");
			System.out.println("                   2 登记收入");
			System.out.println("                   3 登记支出");
			System.out.println("                   4 退    出\n");
			System.out.print("                   请选择(1-4):");

			// 要求用户输入1-4选择
			char menu = Utility.readMenuSelection();
			switch (menu) {
			case '1':
//				System.out.println("显示详情");
				System.out.println("-----------------当前收支明细记录-----------------");
				System.out.println(details);
				
				System.out.println();
				System.out.println("---------------------------------------------");
				
				break;
			case '2':
//				System.out.println("收入情况");
				System.out.print("本次收入金额:");
				int money = Utility.readNumber();//从键盘获取收入的金额
				
				System.out.print("本次收入说明:");
				String desc = Utility.readString();//从键盘获取收入的说明
				
				//根据获取的金额和收入说明修改用户的信息
				balance += money;
				
				details += ("收入\t" + balance + "\t\t" + money + "\t" +desc + "\n");
				
				System.out.println("---------------------登记完成-------------------");
				break;
			case '3':
//				System.out.println("支出情况");
				System.out.print("本次支出金额:");
				int money1 = Utility.readNumber();//从键盘获取支出的金额
				
				System.out.print("本次支出说明:");
				String desc1 = Utility.readString();//从键盘获取支出的说明
				
				//根据获取的金额和支出说明修改用户的信息
				if(balance >= money1){
					balance -= money1;
					details += ("支出\t" + balance + "\t\t" + money1 + "\t" +desc1 + "\n");
					System.out.println("---------------------登记完成-------------------");
				}else{
					System.out.println("余额不足,支出失败");
				}
				
				break;
			case '4':
				System.out.print("确认是否退出(Y/N):");
				char exit = Utility.readConfirmSelection();
				if(exit == 'Y'){
					isFlag = false;
				}
				
				break;

			}

		} while (isFlag);

	}

}

总结:

switch-case

  • 在特殊的场景下,分支结构可以考虑使用switch-case

    • 指定的数据类型:byte \ short \ char \ int ; 枚举类(jdk5.0)\ String (jdk7.0)
    • 可以考虑的常量值有限且取值情况不多。
  • 特别之处:case穿透。

  • 在能使用switch-case的情况下,推荐使用switch-case,因为比if-else效率稍高

Scanner类中提供了获取byte \ short \ int \ long \float \double \boolean \ String类型变量的方法。没有提供获取char类型变量的方法。需要使用next().charAt(0)

Java支持哪些类型循环(上海*睿)

  • for;while;do-while
  • 增强for (或foreach)