流程控制-循环结构
# 前言
前面我们掌握了流程控制的分支结构,接下来学习流程控制的循环结构。
[TOC]
# 循环结构
在某些条件满足的情况下,反复执行特定代码的功能。
# 循环语句分类
for
循环while
循环- do-while循环
在日常开发中,前面两种用得比较多。
# 组成部分
循环语句的四个组成部分:
- 1.初始化部分(init_statement)
- 2.循环条件部分(test_exp),指的是
boolean
类型 - 3.循环体部分(body_statement)
- 4.迭代部分(alter_statement)
# for循环
- 1.初始化部分(init_statement)
- 2.循环条件部分(test_exp),指的是
boolean
类型- 3.循环体部分(body_statement)
- 4.迭代部分(alter_statement)
# 语法格式
for(① 初始化部分;② 循环条件部分;④ 迭代部分){
③ 循环体部分
}
2
3
# 执行过程
for循环的执行过程:1234,234,234,234……2
看下下面代码:
package forTest;
public class Demo1 {
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');
}
}
}
2
3
4
5
6
7
8
9
10
👆面代码的输出是
abcbcbc
注意上面的num是定义在for循环外,所以外面的作用域还是能获取到num值,如果定义在for循环里面,外界是获取不到的。
- 比如:遍历100以内的偶数
package forTest;
public class Demo2 {
public static void main(String[] args) {
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
//System.out.println(i);这里无法获取i,编译无法通过。
}
}
2
3
4
5
6
7
8
9
10
11
12
- 求1-100中所有偶数的和
package forTest;
public class Demo2 {
public static void main(String[] args) {
int total = 0;
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
total += i;
}
}
System.out.println(total);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
结果是:2550。
- 在上面案例的基础上,输出偶数的个数
package forTest;
public class Demo2 {
public static void main(String[] args) {
int total = 0;
int count = 0;
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
total += i;
count++;
}
}
System.out.println("偶数之和:" + total + ",偶数个数:" + count);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
偶数之和:2550,偶数个数:50
# 练习1
遍历1-150,并在每行打印,3的倍数还要在数字后面打印“foo”,5的倍数还要追加打印“biz”,7的倍数还要追加“baz”。
package forTest;
/**
* 遍历1-150,并在每行打印,3的倍数还要在数字后面打印“foo”,5的倍数还要追加打印“biz”,7的倍数还要追加“baz”。
*/
public class Demo3 {
public static void main(String[] args) {
for (int i = 1; i <= 150; i++) {
System.out.print(i);
if (i % 3 == 0) {
System.out.print(" foo");
}
if (i % 5 == 0) {
System.out.print(" biz");
}
if (i % 7 == 0) {
System.out.print(" baz");
}
System.out.println();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
在工作当中,其实你可能一次性写出完美的代码很少,最好是写一点侧一点,不然写了一大堆,比如几千行,才开始测试代码,找bug都能找一天,所以还是写一部分,测一部分,反复修改,让程序完美健壮。
# break关键字
前面我们知道braek在switch-case的作用,其实break可以用在for循环中,它的作用是可以跳出循环。
随机两个正整数m和n(1-100),求最大公约数和最小公倍数。
package forTest;
/**
* 随机两个正整数m和n(1-100),求最大公约数和最小公倍数。
* 比如12和20的最大公约数是4,最小公倍数是60.
*/
public class Demo4 {
public static void main(String[] args) {
//用来存放最大公约数
int k1 = 1;
//用来存放最小公倍数
int k2 = 0;
// int m = (int) (Math.random() * 100 + 1);
// int n = (int) (Math.random() * 100 + 1);
int m = 12, n = 20;
System.out.println("m=" + m + ",n=" + n);
//最大公约数最大整除到m,n之间最小的数
int min = m > n ? n : m;
//从大到小开始遍历,开始整除,如果两个数都能整除遍历的这个数,那么这个数就是最大公约数
for (int i = min; i >= 1; i--) {
//最大公约数,都能够整除i
if (m % i == 0 && n % i == 0) {
System.out.println(i);
System.out.println("m % i=" + (m % i));
System.out.println("n % i=" + (n % i));
k1 = i;
break;
}
}
System.out.println("最大公约数为:" + k1);
//两个数的乘积 =这两个数的最大公约数 * 最小公倍数。
//最小公倍数 = 两个数的乘积 / 这两个数的最大公约数
k2 = m * n / k1;
System.out.println("最小公倍数为:" + k2);
}
}
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
当然,也可以一个个遍历,去求最小公倍数
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;
}
}
2
3
4
5
6
7
# 练习2
打印1~100之间所有奇数的和。
package forTest;
public class Demo5 {
public static void main(String[] args) {
int total = 0;
for (int i = 1; i <= 100; i++) {
if (i % 2 != 0) {
total += i;
}
}
System.out.println(total);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
结果是2500
# 练习3
打印1~100之间所有是7的倍数的整数的个数及总和。
package forTest;
public class Demo6 {
public static void main(String[] args) {
int total = 0;
int count = 0;
//打印1~100之间所有是7的倍数的整数的个数及总和。
for (int i = 1; i <= 100; i++) {
if (i % 7 == 0) {
total += i;
count++;
}
}
System.out.println("total: " + total);
System.out.println("count: " + count);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
total: 735
count: 14
2
# 练习4
输出所有的水仙花数,所谓水仙花数是指一个三位数,其各个位上数字立方和等于其本身。
package forTest;
/**
* 输出所有的水仙花数,所谓水仙花数是指一个三位数,其各个位上数字立方和等于其本身。
*/
public class Demo7 {
public static void main(String[] args) {
for (int i = 100; i < 1000; i++) {
//取出百位数
int bai = i / 100;
//取出十位数
int shi = i / 10 % 10;
//取出各位数
int ge = i % 10;
int total = (int) (Math.pow(bai,3) + Math.pow(shi,3) + Math.pow(ge,3));
if (total == i) {
System.out.println("个位数为:" + ge);
System.out.println("十位数为:" + shi);
System.out.println("百位数为:" + bai);
System.out.println(i);
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
个位数为:3
十位数为:5
百位数为:1
153
个位数为:0
十位数为:7
百位数为:3
370
个位数为:1
十位数为:7
百位数为:3
371
个位数为:7
十位数为:0
百位数为:4
407
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# while循环
循环语句的四个组成部分:
- 1.初始化部分(init_statement)
- 2.循环条件部分(test_exp),指的是
boolean
类型- 3.循环体部分(body_statement)
- 4.迭代部分(alter_statement)
# 语法格式
① 初始化部分(init_statement)
while(② 循环条件部分(test_exp)){
③ 循环体部分(body_statement);
④ 迭代部分(alter_statement);
}
2
3
4
5
# 执行过程
while循环的执行过程基本上和for循环的一致,但是③和④的顺序都是可以自定义的。
# 注意点
- 注意while循环千万小心不要丢了迭代条件,一旦丢了,可能导致死循环。我们写程序要避免死循环。
- for循环和while循环是可以相互转换。
# 和for循环的区别
for循环和while循环的初始化条件部分的作用范围不同。
# do-while循环
循环语句的四个组成部分:
- 1.初始化部分(init_statement)
- 2.循环条件部分(test_exp),指的是
boolean
类型- 3.循环体部分(body_statement)
- 4.迭代部分(alter_statement)
# 语法格式
① 初始化部分(init_statement)
do{
③ 循环体部分(body_statement);
④ 迭代部分(alter_statement);
}while(② 循环条件部分(test_exp))
2
3
4
5
# 执行过程
① - ③ - ④ - ② 然后不停地③ - ④ - ② ……②
③和④的顺序可以交换。
do-while循环至少会执行一次循环体。
# 无限循环
最简单的“无限”循环格式:while(true),for(;😉,无限续航存在的原因是并不知道循环多少次,需要根据循环体内部某些条件,来控制循环的结束。
从键盘读入个数不确定的整数,并判读读入的整数和负数的个数,输入0时结束程序。
package forTest;
import java.util.Scanner;
/**
* 从键盘读入个数不确定的整数,并判读读入的整数和负数的个数,输入0时结束程序。
*/
public class WhileTrueDemo {
public static void main(String[] args) {
//记录正整数个数
int countA = 0;
//记录负整数个数
int countB = 0;
Scanner scan = new Scanner(System.in);
while (true) {
System.out.println("请输入整数:");
if (scan.hasNextInt()) {
int input = scan.nextInt();
if (input > 0) {
countA++;
} else if (input < 0) {
countB++;
} else {
System.out.println("程序结束!");
break;
}
} else {
System.out.println("你输入的不是整数!");
break;
}
}
System.out.println("正整数累计个数为:" + countA);
System.out.println("负整数累计个数为:" + countB);
}
}
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
# 嵌套循环
嵌套循环,将一个循环结构A声明在另一个循环结构B的循环体中,就构成了嵌套循环。
业务场景中可能需要用到,但是一般建议不要超过3层,否则会增加代码复杂度。
两套嵌套循环的情况下,我们一般叫外面的循环为外层循环,里面的循环称为内层循环。
# 练习1
输出4行⭐,每行6个⭐
package forTest;
public class Demo8 {
public static void main(String[] args) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 6; j++) {
System.out.print("*");
}
System.out.println();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
******
******
******
******
2
3
4
# 练习2
输出4行⭐,从1个开始,每行多出一个⭐
package forTest;
public class Demo9 {
public static void main(String[] args) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j <= i; j++) {
System.out.print("*");
}
System.out.println();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
*
**
***
****
2
3
4
# 练习3
⭐⭐⭐⭐
⭐⭐⭐
⭐⭐
⭐
请打印此效果,用嵌套循环实现
分析:
i 行号 | j ⭐个数 |
---|---|
0 | 4 |
1 | 3 |
2 | 2 |
3 | 1 |
规律:i+j=4,那么每行⭐个数为:4-i
package forTest;
public class Demo10 {
public static void main(String[] args) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4-i; j++) {
System.out.print("*");
}
System.out.println();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
# 练习4
⭐
⭐⭐
⭐⭐⭐
⭐⭐⭐⭐
⭐⭐⭐⭐⭐
⭐⭐⭐⭐
⭐⭐⭐
⭐⭐
⭐
分析:
上半部分:
i 行号 | 空格个数 | j ⭐个数 |
---|---|---|
0 | 4 | 1 |
1 | 3 | 2 |
2 | 2 | 3 |
3 | 1 | 4 |
4 | 0 | 5 |
每行空格的个数为:4-i
下半部分:
i 行号 | 空格个数 | j ⭐个数 |
---|---|---|
0 | 1 | 4 |
1 | 2 | 3 |
2 | 3 | 2 |
3 | 4 | 1 |
事实上每一行都是5个字符串,只不过星星的数量不一样。而且只需要拆分为上半部分和下半部分就可以了。
package forTest;
public class Demo11 {
public static void main(String[] args) {
//上半部分
for (int i = 0; i < 5; i++) {
//输出空格
for (int j = 0; j < 4 - i; j++) {
System.out.print(" ");
}
//输出⭐
for (int j = 0; j <= i; j++) {
System.out.print("⭐ ");
}
System.out.println();
}
//下半部分
for (int i = 0; i < 4; i++) {
//输出空格
for (int j = 0; j <= i; j++) {
System.out.print(" ");
}
//输出⭐
for (int j = 0; j <= 3-i; j++) {
System.out.print("⭐ ");
}
System.out.println();
}
}
}
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
*
* *
* * *
* * * *
* * * * *
* * * *
* * *
* *
*
2
3
4
5
6
7
8
9
# 练习5
输出一个九九乘法表
package forTest;
/**
* 久久乘法表
*/
public class Demo12 {
public static void main(String[] args) {
int heng = 1;
int shu = 0;
while (true) {
shu++;
int ji = heng * shu;
System.out.print(heng + " x " + shu + " = " + ji + " ");
if (shu == heng) {
heng++;
shu = 0;
System.out.println();
}
if (heng > 9) {
break;
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
for循环的写法:
package forTest;
public class Demo13 {
public static void main(String[] args) {
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(i + " x " + j + " = " + (i * j) + " | ");
}
System.out.println();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
# 练习6
100以内所有质数的输出。质数,只能被1和它本身整除的自然数。
package forTest;
/**
* 100以内所有质数的输出。质数,只能被1和它本身整除的自然数。
* 最小的质数是2
*/
public class Demo14 {
public static void main(String[] args) {
for (int i = 2; i <= 100; i++) {
boolean flag = true;
for (int j = 2; j < i; j++) {
if (i % j == 0) {
flag = false;
break;
}
}
if (flag) {
System.out.println(i);
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
把数据扩大1000倍,对比一下,不存在break关键字和存在break关键字花费时间
package forTest;
/**
* 100以内所有质数的输出。质数,只能被1和它本身整除的自然数。
* 最小的质数是2
*/
public class Demo15 {
public static void main(String[] args) {
long start = System.currentTimeMillis();
int count = 0;
for (int i = 2; i <= 100000; i++) {
boolean flag = true;
for (int j = 2; j < i; j++) {
if (i % j == 0) {
flag = false;
// break;
}
}
if (flag) {
System.out.println(i);
count++;
}
}
long end = System.currentTimeMillis();
System.out.println("质数个数为:" + count);
System.out.println("花费毫秒:" + ( end-start));
}
}
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
99907
99923
99929
99961
99971
99989
99991
2
3
4
5
6
7
质数个数为:9592,花费时间17054毫秒
打开break关键字,质数个数为:9592,花费时间1592毫秒,省了将近10倍。
那么还能优化代码吗?
其实根本不用整除每一个数,只需要整除i的根号就可以了,为什么?
假设存在一个数96,其实我们知道它可以整除2,所以绝对不是质数。
96能整除2,那肯定能整除48。
能整除3,那肯定能整除32
能整除4,那肯定能整除24
……
能整除6,那肯定能整除16.
能整除的数它们是一对对的,两端都有一个能整除的数字,那么那个临界点就是96的平方根,我只需要遍历到它的平方根,就可以知道了,不用再去重复验证了。
package forTest;
/**
* 100以内所有质数的输出。质数,只能被1和它本身整除的自然数。
* 最小的质数是2
*/
public class Demo15 {
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 2; i <= 100000; i++) {
boolean flag = true;
for (int j = 2; j <= Math.sqrt(i); j++) {
if (i % j == 0) {
flag = false;
break;
}
}
if (flag) {
System.out.println(i);
count++;
}
}
long end = System.currentTimeMillis();
System.out.println("质数个数为:" + count);
System.out.println("花费毫秒:" + ( end-start));
}
}
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
99877
99881
99901
99907
99923
99929
99961
99971
99989
99991
质数个数为:9592
花费毫秒:47
2
3
4
5
6
7
8
9
10
11
12
第一次未优化时间:17054毫秒
第二次优化时间:1592毫秒
第三次优化时间:47毫秒。
# break和continue
关键字 | 使用范围 | 作用 |
---|---|---|
break | switch-case和循环结构中 | 结束当前循环 |
continue | 循环结构中 | 结束当次循环 |
可以看出2个关键字都是用来中断循环,不同的是break
是结束循环后,退出了,而continue
是跳过剩余执行语句,进入下一次循环。
无论break
还是continue
关键字后面不能声明执行语句,编译不通过。
# break
for(int i=1;i<10;i++){
if(i % 4 == 0){
break;
}
System.out.println(i);
}
2
3
4
5
6
123
- 嵌套和break
在嵌套循环中,break
默认结束包裹此关键字最近的一层循环。
# continue
for(int i=1;i<10;i++){
if(i % 4 == 0){
continue;
}
System.out.println(i);
}
2
3
4
5
6
1235679
# 标签
对Java 来说,唯一用到标签的地方是在循环语句之前。进一步说,它实际需要紧靠在循环语句的前方——在
标签和循环之间置入任何语句都是不明智的。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另
一个循环或者一个开关。这是由于 break
和 continue
关键字通常只中断当前循环,但若随同标签使用,它们
就会中断到存在标签的地方。
“标签”是后面跟一个冒号的标识符。
例如:
label:for(int i=1;i<=4;i++){
label2:for(int j=1;j<=10;j++){
if(j % 4 == 0){
break label1;
}
}
System.out.println(i);
}
2
3
4
5
6
7
8
或者
label1:for(int i=1;i<=4;i++){
label2:for(int j=1;j<=10;j++){
if(j % 4 == 0){
continue label1;
}
}
System.out.println(i);
}
2
3
4
5
6
7
8
在嵌套循环中,如果break
或continue
不指定关键字,默认结束包裹此关键字最近的一层循环。
package forTest;
/**
* 100以内所有质数的输出。质数,只能被1和它本身整除的自然数。
* 最小的质数是2
*/
public class Demo16 {
public static void main(String[] args) {
long start = System.currentTimeMillis();
int count = 0;
label:for (int i = 2; i <= 100000; i++) {
boolean flag = true;
for (int j = 2; j <= Math.sqrt(i); j++) {
if (i % j == 0) {
continue label;
}
}
count++;
}
long end = System.currentTimeMillis();
System.out.println("质数个数为:" + count);
System.out.println("花费毫秒:" + ( end-start));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
质数个数为:9592
花费毫秒:14
2
# return
return
:并非专门用于结束循环的,它的功能是结束一个方法,并且返回数据。当一个方法执行了return
,这个方法将结束- 与
break
或者continue
不同的是,return
直接结束整个方法,不管这个return
处于多少层循环之内。