入门篇-其之十-流程控制之循环结构
本文中使用到的工具是Intellij IDEA和JDK 8,需要安装两款工具的请查看这两篇教程:点我查看安装JDK8/11/17教程、点我查看安装Intellij IDEA教程。
假设输出1~100之间的所有整数,正常情况下我们需要写100行代码才能对所有数字输出。
System.out.println(1);
System.out.println(2);
System.out.println(3);
System.out.println(4);
// 其他数字输出省略...
System.out.println(100);
虽然这种办法能达到预期的效果,但是代码量属实有点大(ᇂ_ᇂ|||)
为了解决上述问题,Java为我们提供了一个强大的控制结构——循环结构。
循环结构是一种常用的程序控制结构,它允许程序在执行的过程中反复执行一段代码,直到满足特定条件为止。循环结构可以大大简化重复性任务的编写,提高代码编写效率和可读性。
在Java中,循环结构主要由while
循环、do-while
循环、for
循环组成。
一、while循环
while
循环的语法格式如下:
while (条件表达式) {
执行代码...
}
执行流程:如果条件表达式结果为true
,此时进入while
循环内部执行代码,直到while
循环的条件表达式的结果为false
为止。
while
结构如下图所示:
案例1:使用
while
循环解决1~100的输出和1~100的和
我们先解决1~100的输出,使用while
需要循环100次,我们可以在while
循环外定义一个变量number
并赋值为1,由于是1~100是递增输出,条件表达式需要设定为number <= 100
。
在循环体中,我们先将number
的值进行输出。但是此时number
的值并没有增加,因此每次在输出后需要对number
进行自增1的操作,即number++
,部分代码如下:
int number = 1;
while (number <= 100) {
System.out.println("number = " + number);
number++;
}
这样就完成了1~100的输出,但是我们还需要输出它们相加的和,我们可以对上述的代码进行进一步改造:
在while
循环外部再定义一个变量sum
,默认值为0,用于存储数字相加的和。
在循环体中,输出语句后面实现两数相加的操作:sum = sum + number;
(简写为sum += number;
)
- 第一次循环:sum = 0 + 1 = 1
- 第二次循环:sum = 1 + 2 = 3
- 第三次循环:sum = 3 + 3 = 6
- 以此类推......
在while
循环内部即可完成1~100的计算,最终sum
的结果直接到while
循环外部进行输出即可,完整代码如下:
/**
* while循环--输出1~100并求和
*
* @author iCode504
* @date 2023-11-30
*/
public class WhileLoop1 {
public static void main(String[] args) {
// 1. 定义一个变量number用于输出数字
int number = 1;
// 2. 定义一个变量sum用于存储1~100相加的和
int sum = 0;
// 3. 定义while循环,条件表达式为number <= 100,需要保证number在while循环内相加
while (number <= 100) {
sum += number;
System.out.println("number = " + number);
number++;
}
System.out.println("sum = " + sum);
}
}
运行结果符合我们的预期:
案例2:斐波那契数列
已知每对兔子(一雌一雄)每个月能生殖一对小兔子,每对兔子前两个月没有生育能力,从第三个月每个月能生一对小兔子,假设兔子不存在死亡现象,那么从第一对刚出生兔子开始,第n个月以后会有多少只兔子?
数学解法可以看这位老师的文章:点我查看,我只能说一句:太强了,膜拜大佬!
根据上述题意可以得出:
- 第1个月兔子无生育能力,因此兔子只有1对;
- 第2个月兔子无生育能力,此时兔子仍为1对;
- 第3个月兔子生育了一对兔子,此时兔子为2对;
- 第4个月最开始的兔子又生育了一对兔子,此时兔子为3对;
- 第5个月,最开始的兔子又生育了一对兔子,第三个月的兔子也生育了一对兔子,此时兔子为5对;
由此我们可以得出一个数列:
可以看出,从第三个数起,每个数都是前1个数和前两个数相加得到的和。由此,我们可以得到一个通用公式:
我们可以定义三个变量:number1
、number2
和totalNumber
。其中number1
和number2
存储前两个月兔子的数量,totalNumber
存储前两月兔子数量的和(也就是上述公式的实现),具体实现代码如下:
import java.util.Scanner;
/**
* while循环解决斐波那契数列
*
* @author iCode504
* @date 2023-12-11
*/
public class FibonacciSequence {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入月数: ");
int monthNumber = scanner.nextInt();
// number1和number2代表前两个月兔子的数量
int number1 = 1, number2 = 1;
// i的含义是从第三个月开始繁殖。totalNumber用于计算前两个月兔子的数量
// totalNumber初始化值为1的目的:如果用户输入的月数为1或2,此时兔子对数为1
int i = 3, totalNumber = 1;
// 限制条件:从第三个月开始计算到第monthNumber个月
while (monthNumber > 2 && i <= monthNumber) {
// 计算前两个月兔子总数
totalNumber = number1 + number2;
// number1存储i-2个月兔子数量,用于下一轮循环的运算
number1 = number2;
// number2存储i-1个月兔子数量,用于下一轮循环的运算
number2 = totalNumber;
i++;
}
System.out.println("第" + monthNumber + "个月兔子的数量是" + totalNumber + "对");
}
}
运行结果:
当然,循环只是解决斐波那契数列的一种方式,它还可以使用递归的方式解决。后续讲到方法的时候还会讲到这个问题的递归解法。
二、do-while循环
do-while
循环的语法格式如下:
do {
执行代码...
} while (条件表达式);
执行流程:先执行do
内部的代码,内部代码执行完毕以后,再对while
中的条件表达式进行判断,如果条件表达式为true
,再次进入do
内部执行代码;如果条件表达式为false
,此时就跳出do-while
循环。
和while
循环相比,do-while
循环即使其条件表达式为false
,也会执行一次do
内部的代码,因为它会先执行do
内部的代码再进行条件判断。
do-while
循环的执行流程如下图所示:
案例:计算一个数的所有因子之和(不包括1和自身)。输入一个正整数,输出其所有因子之和。
例如:
- 正整数6的所有因子是:2,3。因此所有的因子的和是\(2 + 3 = 5\)
- 正整数20的所有因子是:2,4,5,10。所有因子的和是\(2+4+5+10=21\)
如果使用do-while
循环来解决这个问题,我们首先需要确定循环的范围是什么?
其实题目中已经告诉我们了一个关键点:不包括1和自身。那么由此可以确定循环的范围是:\([2, number)\)。
如何确定上述区间的数字是正整数number
的因子呢?其实很简单,只需要判断这个数字能否被number
整除即可。
由于我们输入的是正整数,需要在执行循环做一手判断,如果不是正整数,需要给出一个提示。
综合上述内容,代码如下:
import java.util.Scanner;
/**
* do-while解决一个数所有因子的和
*
* @author iCode504
* @date 2023-12-15
*/
public class DoWhileDemo1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入一个正整数: ");
int number = scanner.nextInt();
// 正整数判断
if (number > 0) {
// 定义一个变量sum,用于存储所有因子的和
int sum = 0;
// 由于正整数中因子不包含1和数字本身,因此循环需要从2开始,计算到number - 1
int i = 2;
do {
// 判断条件:i能被number整除的,那么i就是number的因子
if (number % i == 0) {
sum += i;
}
i++;
} while (i < number);
System.out.println("正整数" + number + "所有因子的和是: " + sum);
} else {
System.out.println("您输入的不是正整数!!!");
}
}
}
运行结果:
在开发中,使用do-while
循环的频率要比while
循环少很多。这主要是因为do-while
循环本身是先执行循环体,然后再进行条件判断。即使条件判断结果是false
,do-while
也执行了一次循环体,这可能会导致不必要的计算和操作。
三、普通for循环
普通for
循环的语法格式如下:
for (初始化表达式; 条件表达式; 迭代表达式) {
执行代码...
}
for
循环的执行流程如下图所示:
这里解释一下for
内部的三个表达式:
1. 初始化表达式,指的是初始化Java变量表达式,即数据类型 变量名 = 变量值
例如:int i = 0;
,或者把int i
写到for
循环之前,然后在for
循环第一个位置将i = 0
补齐即可。
当然,初始化表达式也可以同时定义多个变量,例如:int i = 0, j = 0;
2. 条件表达式,如果计算结果为true
,就进入for
循环内部执行代码,如果条件表达式为false
,就会跳出for
循环。
一般而言,条件表达式和初始化表达式中定义的变量有关。例如:i < 100
、i > -30
等等。
3. 迭代表达式,绝大多数情况下针对初始化表达式定义的变量进行增加或减少的操作。
以前面定义的初始化表达式int i = 0;
为例,假设让变量的值增加,我们可以使用如下方式:
i++; // 自增1
++i; // 自增1
i += number; // 让i每次增加number
4. 一般在写for
循环代码时,推荐将初始化表达式、条件表达式、迭代表达式都写上。
事实上,不写上述三个表达式也会执行for
循环代码,但是如果在for
循环内部不写合理的条件判断的话,很容易造成死循环的情况的发生。
那么for
循环的执行流程是怎么样的呢?
- 在首次执行
for
循环的时候,先执行初始化表达式,然后根据条件表达式结果进行判断,如果条件表达式为true
,则进入for
循环内部执行代码,如果条件表达式为false
就跳出for
循环。在执行完for
循环内部代码以后,会针对前面初始化表达式的变量进行迭代操作(常见的是变量相加/相减操作)。 - 后续执行
for
循环的时候,就会根据for
循环迭代表达式的计算结果到条件表达式中进行比较,如果比较结果为true
执行for
内部代码,反之就会跳出for
循环。
这里举两个案例来说明一下for
循环的使用:
案例1:求1~100(包含100)所有的偶数和
解决本题的关键点是偶数的获取,判断一个数是否是偶数,即数字对2求余等于0,再定义一个变量sum
对所有符合条件的偶数累加即可。
/**
* for计算1~100所有偶数和
*
* @author iCode504
* @date 2023-12-15
*/
public class NumberPrint {
public static void main(String[] args) {
int sum = 0;
for (int i = 1; i <= 100; i++) {
// 偶数判断条件:对2求余是否等于0
if (i % 2 == 0) {
// 偶数累加
sum += i;
}
}
System.out.println("1~100之间所有的和: " + sum);
}
}
运行结果:
案例2:水仙花数是指一个3位数,它的每个数位上的数字的3次幂之和等于这个数的本身。例如:
\[1^3 + 5^3+3^3 = 153 \]请使用
for
循环列举所有的水仙花数。
1. 确定范围:水仙花是一个三位数,三位数的数字范围在\([100, 999]\)区间内
2. 个位、十位、百位数的获取:
3. 符合水仙花数的条件:
综合以上的分析,使用for
循环的代码如下图所示:
/**
* for循环列举水仙花数
*
* @author iCode504
* @date 2023-12-15
*/
public class NarcissisticNumber {
public static void main(String[] args) {
// 定义三个变量a, b, c分别存储百位数、十位数、个位数
int a, b, c;
// 循环范围设定在[100, 999] --> [100, 1000)
for (int i = 100; i < 1000; i++) {
// 百位数、十位数、个位数的获取
a = i / 100;
b = i / 10 % 10;
c = i % 10;
// 水仙花数的判断条件
if (i == a * a * a + b * b * b + c * c * c) {
System.out.println(i);
}
}
}
}
运行结果即\([100, 999]\)以内的所有水仙花数:
正如这节标题:“普通for
循环”,其实JDK 5还为我们设计了foreach
循环(也称作增强for
循环),这类循环主要用于遍历数组和列表等结构,使用foreach
循环会简化代码结构,在后续学习数组是我们会用到它,敬请期待°꒰๑'ꀾ'๑꒱°
四、死循环
死循环,即条件表达式计算结果恒定为true
而不断执行循环内部的代码。是一种无法自行终止的循环。在死循环中,程序会反复执行同一段代码,而且永远无法跳出这个循环,除非手动中断程序或者遇到未处理的异常。
下面使用上述三种循环演示以下死循环:
// while形式的死循环
while (true) {
System.out.println("我是死循环");
}
// do-while形式的死循环
do {
System.out.println("我是死循环");
} while (true);
// for形式的死循环:不写初始化表达式和迭代表达式,条件表达式为true
for (; true; ) {
System.out.println("我是死循环");
}
运行代码的过程中,如果不进行人为停止程序,它们就会不停输出“我是死循环”:
死循环会导致程序无法正常执行其他任务,并且可能会占用大量资源。在编写循环结构时,务必要保证正确设置循环条件和提供适当的跳出机制,以避免死循环的发生。
五、Intellij IDEA关于循环的快捷键
1. 在Intellij IDEA中,如果快捷生成一个循环结构,可以输入fori
,然后会出现一个提示,按回车即可生成一个for
循环结构。这个for
循环是从0开始的:
2. 如果想生成一个从0~n的for
循环,可以使用n.for
快捷生成。例如:如果我想生成0~100之间的循环,可以直接按100.for
,出现提示后直接按回车即可生成这个区间的for
循环:
3. 输入n.forr
可以生成一个从n到0依次递减的for
循环,还是以100.forr
为例,生成的效果如下图所示:
4. 在编写代码的过程中,如果条件表达式可以确定的话,可以直接使用条件表达式.while
快捷生成一个while
循环,例如: