乐趣区

关于java:循环结构了解Java爱的魔力转圈圈

循环构造

你好,欢送回来,我是 BoBo!HAKUNA MATATA!!!

紧接着上次课的抉择构造,明天学习流程管制的另外一种状况:循环构造。“循环”就是事物周而复始的静止或变动的景象,生存中这样的景象不可胜数。比方海陆空物流零碎、自然界的水循环系统,还有废弃物回收利用的环保零碎等等:

Java 语言与现实生活是紧密联系的,因而,在 Java 语言中也有 让代码反复执行 的循环构造。

代码不会平白无故地反复执行,想要让代码反复执行,必须要满足肯定的条件;此外,代码反复执行多少次呢?如果无限度的反复执行上来,其它的代码将永远也得不到执行的机会,这必定不是咱们想要看到的,还得想方法控制代码执行的次数。因而,想要弄明确循环构造,咱们必须得想分明两件事:

  • 代码反复执行的条件
  • 如何控制代码反复执行的次数

Java 语言为咱们提供了三种根本的循环构造:forwhiledo...while,绝大部分时候三种循环都能实现雷同的成果,也就是说,它们是能够互相替换的,但因为彼此格局上轻微的差别,决定了不同的循环更适宜于不同的场景。本次课程将为你介绍这三种循环的格局和用法,心愿你重点关注它们的差别,以便把它们用在最合适的场景。

通过本次课程的学习,你将会有以下播种:

  1. 应用 Java 语言中三种根本循环构造(forwhiledo...while)实现代码的反复执行

    1. 在循环构造中依据肯定条件管制循环是否持续或终止
    2. 在简单的循环构造中进行跳转

本次课程的内容如下:

  • ​ 循环:爱的魔力转圈圈

    ​ for 循环

    ​ while 循环

    ​ do…while 循环

  • ​ 循环终止:不带走一片云彩
  • ​ 标号:循环跳转的骚走位

第一关 循环:爱的魔力转圈圈

1.1 for 循环

开发中用的最多的是 for 循环,并非它有多非凡,习惯而已。看一个需要:把“爱的魔力转圈圈”输入 5 遍。你当然能够写 5 次输入语句,然而太 low,你好意思写,我都不好意思看。应用 for 循环该怎么做呢?上代码:

public class Test{public static void main(String[] args) {for (int i = 1; i <= 5; i++) {System.out.println("爱的魔力转圈圈");
        } 
    }
}

输入:

爱的魔力转圈圈
爱的魔力转圈圈
爱的魔力转圈圈
爱的魔力转圈圈
爱的魔力转圈圈

这段代码看起来简略又简单:简略指的是输入语句只有一句,简单指的是,for 循环的申明看起来内容很多,而且跟以前每句代码独占一行的写法不同。没错,咱们把 for 循环的申明提取出根本格局:

public static void main(String[] args) {for (初始化语句; 判断条件; 管制条件) {// 循环体}
}

从整体来看,for 循环的申明与 if 语句的申明,构造上相似:关键字 + 小括号 + 大括号,只不过 if 语句中的小括号里只有关系表达式,而 for 循环小括号里用两个分号(;)将三条语句隔开:初始化语句、判断条件、管制条件,这里的“判断条件”就是一个关系表达式,返回后果也是 boolean 类型,与 if 语句里的统一;前面紧跟着的一对大括号,外面是要反复执行的代码,叫做“循环体”,这就是 for 循环申明的固定格局。

初始化语句:for 循环执行须要的初始数据,个别都是变量的定义。

判断条件:代码反复执行的条件,意思是,条件成立就会反复执行,不成立就终止循环。

管制条件:管制整个循环是否继续执行上来的条件,个别是对“判断条件”的数据的批改。

循环体:要反复执行的代码。

那这个 for 循环是如何执行的呢?来看看它的执行流程:

对照上图和代码。

for 循环开始,会首先执行初始化语句,实现所需数据的定义和初始化;紧接着执行判断条件,此时,判断条件有可能成立,也有可能不成立:如果条件不成立(判断条件返回 false):循环立刻完结;反之,如果条件成立(判断条件返回 true):执行循环体,这时,会把循环体中所有代码执行一遍,而后,执行管制条件,到此为止,第一次循环执行完结,打印了一行信息:爱的魔力转圈圈。很显著,for 循环并没有终止执行,接下来,它继续执行判断条件,查看循环继续执行的条件是否成立,同样的:如果条件不成立(判断条件返回 false):循环立刻完结;反之,如果条件成立(判断条件返回 true):执行循环体,这时,会把循环体中所有代码再执行一遍,而后,再执行管制条件,到此为止,第二次循环执行完结,再一次打印了一行信息:爱的魔力转圈圈。就这样始终反复上来,直到判断条件不成立,循环完结。

从下面的执行流程能够看出,判断条件 的成立与否,是决定循环体是否执行的要害,初始化语句给判断条件提供了初始数据,而管制条件通过批改判断条件应用的数据,从而管制判断条件是否成立,进而影响循环体的反复执行。

那么请问,假如for 循环最大可执行 N 次,for 循环中的初始化语句、判断条件、管制条件和循环体在整个循环执行的过程中,别离会执行多少次?

  • 初始化语句:有且仅有 1 次。
  • 判断条件:至多 1 次,即 1 - N 次。当第一次判断不成立的时候,循环完结,所以只执行一次;如果成立,则在下次循环开始还会进行判断。
  • 管制条件:0- N 次。0 次,是指第一次判断条件就不成立。
  • 循环体:0- N 次。0 次,同上。

做个练习试试手。

需要:应用 for 循环在控制台输入 1 -5

剖析:for 循环对你来说还是个新常识,要应用它,首先搞明确几件事:

  1. 咱们要做的事是什么,即 循环体 是什么:打印从 1 到 5 的数字。

    1. 咱们要做的事有什么法则,即循环体要 执行几次 每次执行之间的分割:执行 5 次,打印数字每次递增 1。
    2. 如何决定循环体是否执行,即 判断条件成立的依据 是什么:判断执行的次数,最大不能超过 5 次。
    3. 如何管制判断条件成立与否,即 管制条件如何批改判断条件的数据:管制次数,逐次递增 1.

好了,依据下面的剖析和 for 循环的根本格局能够得出实现这个需要的步骤:

  1. 定义整型变量 number,示意要打印的数字,初始值是 1,最大值是 5,每打印一次之后都须要加 1:

    int number= 1;

  2. 定义整型变量 time,示意循环体执行的次数,作为 for 循环的初始化语句,初始值是 1:

    int time = 1;

  3. 循环最多执行 5 次,所以变量 time 的最大值是 5,即 for 循环的判断条件:

    time <= 5;

  4. 每打印一次数字,次数都须要加 1,所以,for 循环的管制条件:

    time++

  5. 在循环体中打印数字,而后让数字加 1:

    System.out.println(number);

    number++;

残缺代码如下:

public class Test{public static void main(String[] args) {
        // 1. 要打印的数字,初始值是 1,最大值是 5,每打印一次之后都须要加 1
        int number = 1; 
        /*
           2. 定义整型变量 time,示意循环体执行的次数,作为 for 循环的初始化语句,初始值是 1
           3. 循环最多执行 5 次,所以变量 time 的最大值是 5,即 for 循环的判断条件:time <= 5
           4. 每打印一次数字,次数都须要加 1,所以,for 循环的管制条件:time++
        */
        for (int time = 1; time <= 5; time++) {
            // 5. 在循环体中打印数字,System.out.println(number);
            number++; // 而后让数字加 1
        }
    }
}

输入后果:

1
2
3
4
5

数字曾经正确输入,完全符合需要。

聪慧的你可能曾经发现,变量 timenumber 的初始化、变化规律齐全一样!没错。那能不能优化一下代码,只应用一个变量呢?答案是必定的:

public class Test{public static void main(String[] args) {for (int number = 1; number <= 5; number++) {System.out.println(number);
        }
    }
}

这个时候,number 变量既代表打印的次数,又代表每次打印的数字,两全其美。学习的过程不仅要弄明确常识的前因后果,还要有本人的思考。代码看起来很简略,每个组成部分的起源和意义才是要害。当你可能用本人的形式把常识之间的关系联结起来,你才算是学会了。

考一考:for 循环的应用

1. 需要:应用 for 循环输入 1 - 5 之和

剖析:

  1. 定义一个变量 sum,代表 1 到 5 的和,初始化值是 0
  2. for 循环获取 1 - 5 的数据
  3. 把每一次获取到的数据累加到变量 sum

    sum = sum + x; 或者 sum += x;

  4. 循环完结,输入变量 sum 的值

答案:

public class Test{public static void main(String[] args) {
        // 1. 定义求和变量 sum.
        int sum = 0;
        // 2. 通过 for 循环获取 1~5 之间的数据.
        for (int i = 1; i <=5; i++) { // i 记录的就是: 1~5 之间的数字
            // 3. 把获取到的数据顺次累加给变量 sum
            sum += i; // sum = sum + i;
        }
        // 4. 打印后果
        System.out.println("sum =" + sum);
    }
}

2. 需要:求出 1 -100 之间偶数和

剖析:

  1. 定义一个求和变量 sum,初始化值是 0
  2. 获取 1 -100 之间的数,用 for 循环实现
  3. 判断每一个数是否为偶数,是就累加,否则不做操作

    对 2 取余等于 0,则为偶数:x % 2 == 0

  4. for 循环完结,输入求和变量 sum 的值

答案:

public class Test{public static void main(String[] args) {
        // 1. 定义一个求和变量 sum
        int sum = 0;
        // 2. 获取 1~100 之间所有的数据
        for (int i = 1; i <= 100; i++) { // i 的值其实就是 1~100 之间的数字, 只有判断 i 是否是偶数即可
            // 3. 判断以后获取到的数据是否是偶数, 是就累加
            if(i % 2 == 0) {
                // 能走到这里, 阐明 i 是偶数, 累加即可
                sum += i;
            }
        }
        // 4. 打印后果
        System.out.println("sum:" + sum);
    }
}

3. 需要:在控制台输入所有的”水仙花数”

剖析:

​ 水仙花数:所谓的水仙花数是指一个三位数,其各位数字的立方和等于该数自身
​ 举例:153 是一个水仙花数:1 1 1 + 5 5 5 + 3 3 3 = 1 + 125 + 27 = 153

步骤:

  1. 获取所有的三位数,即 100-1000 之间的数
  2. 获取每一个三位数的个位,十位,百位
    ​ 个位:153 % 10 = 3
    ​ 十位:153/10%10 = 5
    ​ 百位:153/10/10%10 = 1
  3. 拿个位,十位,百位的立方和与该数自身进行比拟,如果相等,则在控制台打印该数

答案:

public class Test{public static void main(String[] args) {
        // 1. 通过 for 循环, 获取所有的三位数.
        for (int i = 100; i < 1000; i++) { // i 示意的就是所有的三位数
            // 2. 获取该数据的个位, 十位, 百位数字.
            int ge = i % 10;
            int shi = i / 10 % 10;
            int bai = i / 10 / 10 % 10;

            // 3. 判断该数字是否是水仙花数, 如果是, 间接打印即可
            if (ge * ge * ge + shi * shi * shi + bai * bai * bai == i) {
                // 能走到这里, 阐明 i 是水仙花数
                System.out.println(i);
            } // 不是水仙花数,不做任何操作
        }
    }
}
public class Test{public static void main(String[] args) {
        // 1. 通过 for 循环, 获取所有的三位数.
        
            // 2. 获取该数据的个位, 十位, 百位数字        

            // 3. 判断该数字是否是水仙花数, 如果是, 间接打印即可
            
    }
}

4. 需要:统计所有的”水仙花数”的个数

剖析:

  1. 定义计数器变量 count,初始化值为 0
  2. 应用 for 循环获取所有的三位数,即 100-1000 之间的数
  3. 判断每一个三位数是否为水仙花数,是则 count 自增 1
    count ++;
  4. 循环完结,输入计数器 count 的值

答案:

public class Test{public static void main(String[] args) {
        // 1. 定义一个计数器, 用来记录水仙花数的个数
        int count = 0;
        // 2. 获取到所有的三位数
        for (int i = 100; i < 1000; i++) { // i 记录的就是所有的三位数
            // 3. 获取到该数字的个位, 十位, 百位数字.
            int ge = i % 10;
            int shi = i / 10 % 10;
            int bai = i / 10 / 10 % 10;

            // 4. 判断该数字是否是水仙花数, 如果是, 计数器自增 1.
            if (ge * ge * ge + shi * shi * shi + bai * bai * bai == i) {
                // 能走到这里, 阐明 i 是一个水仙花数
                ++count;
            }
        }
        // 5. 打印计数器的后果即可
        System.out.println("水仙花数的个数是:" + count);
    }
}
public class Test{public static void main(String[] args) {
        // 1. 定义一个计数器, 用来记录水仙花数的个数
        
        // 2. 获取到所有的三位数
        
            // 3. 获取到该数字的个位, 十位, 百位数字.
            
            // 4. 判断该数字是否是水仙花数, 如果是, 计数器自增 1.
            
        // 5. 打印计数器的后果即可
        
    }
}

1.2 while 循环

从语法的角度上讲,所有的 for 循环都能够用 while 循环改写。只不过两种循环的格局不同,实用场景有所差异。来看下 while 循环的格局:

    初始化语句;
    while (判断条件) {
         循环体;
         管制条件;
    }

while 循环的 初始化语句 管制条件 不像 for 循环那样在小括号外面,这是二者最大的不同。依照这种格局,能够很容易的把 for 循环的代码改写成 while格局(打印 1 - 5 的数字):

public class Test{public static void main(String[] args) {
        int number = 1;    // 初始化语句
        while (number <= 5) { // 判断条件
            System.out.println(number); // 循环体
            number++; // 管制条件
        }
    }
}

输入后果没什么不同:

1
2
3
4
5

while 循环的执行流程和 for 循环简直齐全一样,只不过因为初始化语句的地位不同,while 循环的初始化语句优先执行之后,才真正进入 while 循环的地界,后续的执行流程与 for 循环完全一致,贴个图了事,这里就不赘述了:

你可能会说,既然两种循环构造任何时候都能够调换,而且执行流程也一样,为啥还弄俩,一个不就行了吗?尽管看起来是这样,然而你有没有留神到,while 循环的初始化语句在整个循环构造的里面,由此看来,它并非是 while 循环必不可少的一部分;同样的,管制条件在循环体的外部,换句话说,它也能够认为是循环体的一部分啊!照这么说,while 循环的初始化语句和管制条件语句都是能够省略的。换句话说,while 循环相当于 for 循环的简洁模式。

“哦,原来如此。然而彭彭,有个 Bug。”

“说来听听。”

“你不是说 for 循环和 while 循环任何时候都能够调换吗?”

“是的。”

“省略了初始化语句和管制条件的 while 循环,怎么转成 for 循环?”

“聪慧。小伙子反馈还挺快。”

其实在 for 循环申明格局里,小括号外面的三条语句都能够省略——然而两个分号(;)不能省。如果省略了三条语句,整个 for 循环看起来赤裸裸的,像没穿衣服的小屁孩(是不是想到了小时候的本人):

     for (; ;) { // 死循环
        // 循环体
    }

这种格局的 for 循环是一种最简略的 ” 死循环 ”——本节最初介绍。省略了初始化语句和管制条件的 while 循环,只须要把判断条件放到 for 循环对应的地位,就改写成了 for 循环:

     for (; 判断条件;) {// 循环体}

比照 for 循环,while 循环相当于简写模式,也就是说,当咱们不须要“初始化条件”和 / 或“管制条件”的时候,就能够应用 while 循环,因为它的格局更加简洁。

好了,当初交给你个工作,把后面介绍的所有 for 循环的代码改成 while 循环的格局。

1.3 do…while 循环

老夫子教诲咱们:学而时习之。学完一个常识,要多练习。刻意练习才可能游刃有余,如果练一遍不行,那就练两遍、三遍,然而,至多要练一遍,直到学会为止。否则,常识仅仅在脑海中记忆犹新般过了一遍,没有留下粗浅的印象,就不算实现了学习。屡次练习其实就是反复执行,咱们能够用循环来实现,然而如何确保“至多练一次”呢?

Java 语言提供了一种非凡的循环构造:do...while 循环,这个构造的非凡之处在于,它会让循环体先执行(do)一遍,而后去判断条件是否满足,再依据理论须要决定是否持续循环。这种格局的循环完满实用下面的需要。

来看看神奇的 do…while 循环语句的格局:

    初始化语句;
    do {
         循环体;
         管制条件;
    } while (判断条件); 

留神 while 小括号最初的分号不可省略。与 while 循环雷同的中央是,初始化语句在整个循环构造的下面,管制条件仍然在循环体的最初;不同的中央在于,循环体和判断条件的地位产生了调换,这也就意味着,先执行循环体和管制条件,再执行判断条件。

它的执行流程是这样的:

  1. 先执行初始化语句。这一步与 while 循环齐全一样。
  2. 执行循环体。
  3. 执行管制条件。
  4. 执行判断条件:

    条件成立,则再一次执行循环体、管制条件等内容

    条件不成立,循环终止。

因为 do...while 循环的判断条件放在最初,所以它的循环体局部必然能执行,或者说,do...while 循环的循环体至多执行一次,这是它与前两个循环最大的不同。

回到后面提到的需要:如何用 do…while 循环实现“学完一个常识,至多练习 1 次”?

剖析:假如练习三次之后肯定能学会,那么,do...while 循环的各个局部如下:

​ 1. 初始化条件:定义 int 型变量 count,代表练习的次数,初始化值为 1

​ 2. 判断条件:定义 boolean 型变量 isOK,作为一个标记,代表是否学会,默认值为 false

​ 3. 循环体:

​ 判断当练习次数小于等于 3 时,打印正在练习的次数

​ 每练习一次,次数加 1:count ++

  1. 管制条件:当次数大于 3,示意已学会:给标记从新赋值:isOK = true

示例代码:

public class Test{public static void main(String[] args) {
        // 1. 定义一个变量, 记录练习次数
        int count = 1;
        // 2. 定义一个变量, 用来标记是否学会这个知识点. true: 学会了, false: 没学会
        boolean isOK = false;
        do {if (count <= 3) { // 3. 判断当练习次数小于等于 3 时,System.out.println("正在进行第" + count + "次练习"); //  打印正在练习的次数
                count++; // 每练习一次, 次数要 +1
            } else { // 4. 当次数大于 3,示意已学会
                isOK = true; // 将 boolean 类型变量的值改为: true
            }
        } while (!isOK); // 判断标记不为 false,即没学会的时候,循环执行
    }
}

输入后果:

正在进行第 1 次练习
正在进行第 2 次练习
正在进行第 3 次练习

这就是 do...while 循环的根本应用。

比照这三种循环:从格局上看,while 循环相当于 for 循环的简化格局,要晓得,这两种循环任何时候都能够互相替换。之所以会呈现两个不同格局,是因为有些场景的循环操作并不需要初始化条件,对管制条件的须要也没那么强烈,简化版循环 while 就显得构造更清晰,可读性更强。对于 do...while 循环来说,它在格局上相似 while 循环,只不过二者循环体和判断条件的地位正好相同,也是这个起因,使得 do...while 循环的循环体局部先于判断条件执行,即至多执行一次。

综上所述,个别状况下,咱们尽可能应用 for 循环,如果不关注循环的初始化条件,能够应用简化版的 while 循环,当你的需要必须要执行一次循环体的时候,应用 do...while 循环。

到此为止,这三种循环的根本应用介绍完了,心愿你多练习案例代码,尝试互相转换三种循环的代码。

第二关 循环终止:不带走一片云彩

2.1 break

如果对一组数据的 每一条 都要进行解决,那么遍历所有的数据是必要的;但如果是为了查找一组数据中的 某一个,就不总是循环所有的次数,因为可能在任何一次循环的两头找到须要的数据。这个时候,对后续数据的遍历就不再必要了。为了进步性能,同时也为了节省时间,找到须要的数据之后就终止循环才是最合适的做法。

那么,如何在循环执行的过程中去终止它呢?

答案是:应用 break; 语句。

你应该对 break; 语句不生疏了吧,因为在《抉择构造》课程中介绍的 switch 构造中,就是通过 break; 语句来完结的,同样的,在循环构造中,也能够应用它来完结。用法非常简单,在循环体中的任何一个地位,只有你认为能够完结循环,那么就能够把这个语句放到这里。

举个栗子演示一下。

比方当初有这样一个需要:班级中有 15 位同学,请查找编号为 3 的同学。

剖析:查找的过程是一个反复操作,应用 for 循环来实现。咱们假如班级中同学们的编号是 1 -15,那么遍历每个同学,查看他们的编号,如果为 3,就终止循环。如果不是,就持续查找,直到找到 3 号同学为止。由此,循环的各个组成部分:

​ 1. 初始化语句:定义同学的编号,从 1 开始,最大 15

int number = 1;

​ 2. 判断条件:遍历每个同学的编号,最大到 15 完结,所以编号的范畴是 1 -15

number <= 15;

​ 3. 管制条件:每遍历一位同学,编号就加 1

number++

​ 4. 循环体:

​ 判断同学编号是否为 3
​ 若该同学编号为 3,则打印该同学编号,完结循环
​ 若该同学编号不为 3,不做任何操作

示例代码:

public class Test{public static void main(String[] args) {
        // 遍历编号为 1 -15 的所有同学
        for (int number = 1; number <= 15; number++) {if (number == 3) { // 如果某位同学的编号为 3
                System.out.println("找到了编号为 3 的同学"); // 打印信息
                break; // 完结循环
            } // 否则,不作任何操作,让循环持续,进行下一次查找
        }
    }
}

代码非常简单。咱们在循环体里退出一个 if 判断语句:当编号为 3 时,打印同学信息,而后应用 break; 语句完结循环,否则,不做任何操作,让循环持续,进行下一次查找,直到找到为止。变量 number 的值从 1 开始,那么必定是到第三次循环的时候找到了对应的同学,而后整个循环被 break; 语句终止了。

是不是很简略!

2.2 continue

另一种状况,在遍历一组数据的时候,并不是要取出那些非凡的数据,而是跳过它们,因为它们可能是非法的、或者是因为测试而填入的数据。那么,怎么在循环执行的过程中跳过这个数据呢?

答案是:应用 continue; 语句。

再举个栗子。

需要:一起来玩逢 7 必过小游戏。游戏规则:多人围坐在一起,顺次疾速说出从 1 -100 的数字,所有含 7、7 的倍数的数不能说,否则就失败受到惩办。

剖析:

    1. 同样,应用 for 循环遍历 1 -100 的数,

​ 2. 而后在循环体中,判断以后遍历的数中是否含 7、或是否为 7 的倍数

​ 是否含 7,包含个位是 7 和十位是 7 两种状况:

​ 个位是 7:对 10 取模,余数为 7:number % 10 == 7

​ 十位是 7:除以 10,商为 7:number / 10 == 7

​ 是否为 7 的倍数:对 7 取模,余数为 0:number % 7 == 0

​ 3. 跳过所有合乎上述要求的数:continue;

    4. 打印其它数。打印成果:

示例代码:

public class Test{public static void main(String[] args) {
        // 1. 通过 for 循环获取到 1~100 之间所有的数据
        for (int number = 1; number <= 100; number++) {
            // 2. 蕴含 7 或者是 7 的倍数, 这些数据都要跳过
            if (number % 10 == 7 || number / 10 == 7 || number % 7 == 0) {
                // 3. 符合要求,间接跳过以后数(跳过本次循环)continue;
            }
            // 4. 如果数据非法, 间接打印即可
            System.out.println(number);
        }
    }
}

程序运行过程中,如果通过了 if 语句的判断,就会执行 continue; 语句,它的作用是:跳过本次循环,进行下次循环。

break; 语句和 continue; 语句都有“完结”循环的意思,但它们有显著的区别:

break:完结所在循环的遍历操作。它终止了 整个循环,不再进行循环遍历操作。

continue:跳过本次循环,持续下次循环。它终止了整个循环操作的 某一次,而后持续下一次循环操作。

2.3 死循环

摸索人生的路线上,咱们不晓得失败了多少次,而且,在不远的将来,咱们可能还会再一次遭逢失败。然而——后面都是废话,无论失败多少次,咱们仍然保持尝试,不论须要保持多久,咱们都不会放弃,直到找到胜利前途的那天。

说的我本人都快信了。

尽管咱们的人生方向并不迷茫,但咱们并不明确的晓得获得成功的具体日期,所以,咱们不得不一次又一次的尝试,不晓得还需多少次,何时能力破局而出,升职加薪、走上人生 癫疯。其实,我是想说,有些时候,反复的次数是无奈预知的,而后面介绍的循环,都明确晓得循环的最大次数。在不晓得应该反复执行多少次的时候,如何用循环来实现、又如何终止循环呢?

答案:用死循环来实现,而后在满足某种非凡条件时,应用 break; 语句进行终止。

“死循环”的意思,是指循环自身不会主动终止,而是须要依附其它的条件能力完结。后面介绍的循环,最终都会因为判断条件不成立而终止,可死循环的判断条件永远都是成立的。三种简略的死循环格局如下:

通过上图能够看出,for 循环的死循环格局,要求判断条件为空,或者恒成立,初始化语句和管制条件并没有作任何要求;while 循环和 do...while 循环的判断条件都是 boolean 类型的常量:true,这样也能保障判断条件的恒成立。这就是三种循环的简略死循环格局。

咱们并不会容忍死循环永远的执行上来,只不过因为不晓得这种循环具体的执行次数,才不得不应用这种格局。所以,个别状况下,咱们都会在循环体外部设置某些条件,当条件满足就让循环终止,而不是任由它节约 CPU 的资源。

举个栗子。

需要:假如纸张厚度为 0.001 米,喜马拉雅山高度为 8848 米。请问:须要将纸张对折多少次,能力达到喜马拉雅山的高度?

剖析:因为不晓得循环多少次,用死循环。就应用格局简洁的 while 循环,你也能够改成另两种格局。每次循环让厚度加一倍,即厚度乘以 2,这里的厚度是在上一次折叠之后的根底上,所以是累乘的成果。

  1. 定义 int 变量 count 作为折叠计数器,代表须要折叠的次数,初始值为 0,每折叠一次加 1
  2. 定义 double 变量 thicknessheight,别离代表纸张厚度和喜马拉雅山高度,初始值别离是 0.001 和 8848
  3. 循环体:

    判断以后纸张厚度是否达到山的高度:thickness >= height

    如果已达到,即判断条件返回 true,终止循环:break;

    否则,

    ​ 进行纸张的折叠:thickness *= 2;

    ​ 每折叠一次,让计数器加 1:count++;

  4. 循环完结,打印折叠计数器的值

示例代码:

public class Test{public static void main(String[] args) {
        // 1. 定义折叠次数,每次加 1
        int count = 0; 
        // 2. 定义纸张厚度和喜马拉雅山高度
        double thickness = 0.001; // 纸张厚度
        double height = 8848; // 喜马拉雅山高度

        while (true) { // 不晓得循环多少次,用死循环
            // 3. 判断条件,以后纸张厚度是否达到山的高度
            if (thickness >= height) {break; // 如果已达到,则终止循环}
            // 否则。如果 if 条件成立,必然跳出循环,if 语句体里的代码不会与这里的代码同时执行。thickness *= 2; // 进行纸张的折叠
            count++; // 计数器加 1
        }
        // 4. 循环完结,打印折叠计数器的值
        System.out.println(count);
    }
}

输入后果:

24

须要折叠 24 次,不信你能够试试。

如果死循环使用不当,比方你设置的完结条件始终无奈达成,那么这个循环体代码可能会始终执行上来,从而导致 CPU 资源耗尽,所以,应用死循环肯定要谨慎,确保使它跳出的条件肯定可能执行到,否则,期待你的将是 ……,你本人品。

期待你破局而出的日子。

第三关 标号:循环跳转的骚走位

3.1 循环嵌套

遍历一组数据对你来说没有什么挑战性,当初,减少一点难度:遍历多组数据。

需要:假如当初有 3 个班级,每个班级 15 名同学,如何获取所有班级中的同学呢?

这个需要里有两组数据:一、3 个班级;二、每个班级 15 名同学。如果要找到所有的同学,能够一个班一个班地找,每找一个班,再一一把该班级所有同学找进去就行了。看起来逻辑并不简单,该怎么实现?

剖析:通过观察可知,班级和同学这两组数据之间有肯定的关系:班级蕴含同学。遍历每个班级须要用到循环,同样,遍历每个同学也须要用到循环,莫非循环之间也能够有蕴含关系吗?是的,咱们能够 在一个循环体语句中蕴含另一个循环语句 ,这种状况称为 循环嵌套 。班级循环称为 外层循环 ,被蕴含的同学循环称为 内层循环。咱们先来尝试把外层循环实现进去:

  1. 初始化条件:定义 int 型变量 classNumber,初始值为 1,最大值为 3;

    1. 判断条件:classNumber <= 3;
    2. 循环体:打印班级编号
    3. 管制条件:classNumber++;

示例代码:

public class Test{public static void main(String[] args) {for (int classNumber = 1; classNumber <= 3; classNumber++) { // 外层循环,遍历每个班级
            System.out.println(classNumber); // 打印班级编号
        }
    }
}

输入:

1
2
3

当初,咱们再把内存循环实现进去,也就是把一个班级的同学编号打印进去:

 1. 初始化条件:定义 `int` 型变量 `studentNumber`,初始值为 1,最大值为 15;2. 判断条件:`studentNumber <= 3;`
 3. 循环体:打印同学编号
 4. 管制条件:`studentNumber++;`

示例代码:

public class Test{public static void main(String[] args) {for (int studentNumber = 1; studentNumber <= 15; studentNumber++) { // 内层循环,遍历每个同学
            System.out.println(studentNumber); // 打印同学编号
        }
    }
}

运行输入:

1
2
3
...
14
15

好了,两层循环都实现了,怎么把它们蕴含起来呢?

其实非常简单,咱们的要求是:在遍历每个班级的时候去查找该班级的所有同学,所以,内存循环——即遍历同学的循环,其实是外层循环的一部分,即循环体:

public class Test{public static void main(String[] args) {for (int classNumber = 1; classNumber <= 3; classNumber++) { // 外层循环,遍历每个班级
            for (int studentNumber = 1; studentNumber <= 15; studentNumber++) { // 内层循环,遍历每个同学
                System.out.println("正在获取的第" + classNumber + "个班级的第" + studentNumber + "位同学"); // 打印同学编号
            }
        }
    }
}

输入:

正在获取的第 1 个班级的第 1 位同学
正在获取的第 1 个班级的第 2 位同学
正在获取的第 1 个班级的第 3 位同学
...
正在获取的第 2 个班级的第 1 位同学
正在获取的第 2 个班级的第 2 位同学
...
正在获取的第 2 个班级的第 14 位同学
正在获取的第 2 个班级的第 15 位同学
正在获取的第 3 个班级的第 1 位同学
正在获取的第 3 个班级的第 2 位同学
正在获取的第 3 个班级的第 3 位同学
...
正在获取的第 3 个班级的第 13 位同学
正在获取的第 3 个班级的第 14 位同学
正在获取的第 3 个班级的第 15 位同学

就这样,咱们通过循环嵌套实现了 3 个班级共 45 名同学的查找。

循环嵌套并没有想像中的那么简单,在设计双层循环构造的时候,首先要想分明每层循环所代表的含意,而后别离专一于每层循环代码的实现就能够了。你只须要把内层循环作为外层循环的循环体,在编写内层循环代码的时候,就把它当作一般的循环,与外层循环无关,剥离外层循环的烦扰,专一于内层循环的实现。还要留神一个细节,两层循环应用的变量名不要反复,要不然很容易凌乱,各自独立是最好的。

3.2 标号

略微改一下需要:A 公司邀请程旭元同学退出,旭元同学还在班级里埋头狂敲代码,现按班级查找程旭元同学。有 3 个班级,每班 15 个同学,假如第 3 个班级的第 10 位同学名叫程旭元,找到该同学后则进行查找。怎么实现?

必定还是要应用双层嵌套循环,外层循环和内层循环的含意没变,问题是,查找同学的操作在内层循环,找到程旭元同学后怎么让两层循环同时终止?咱们晓得完结单层循环的形式:break; 语句,却没有方法完结双层循环,况且是外层循环。

这确实是个难题。

然而难不倒聪慧的彭彭。break; 语句默认状况下的作用是完结以后循环,如果给它一种能力,让它能够完结外层循环不就行了嘛!

怎么做呢?能够给外层循环加一个名字,同时,在执行 break; 语句的时候把这个名字带上,意思是“完结指定名字的循环”。这种名字叫“标号”,即循环的名称。咱们给循环定义一个标号,就能够依据须要完结或跳转到指定循环,它的定义格局是这样的:

    标号: for () {}    // 标号的定义,间接在循环前写标识符。while 和 do…while 举例略

或者这样,让标号独占一行:

    标号:
    for () {}         // 标号的定义,间接在循环前写标识符。while 和 do…while 举例略

应用的时候,只须要在 break 关键字前面跟上标号就能够了,就像这样:

    break 标号;        // 完结名称为指定标号的循环

或者这样,应用 continue 关键字加标号,意思是跳转到指定标号的循环继续执行:

    continue 标号;    // 跳转到名称为指定标号的循环继续执行

标号是一种标识符,定义的时候必须合乎标识符的命名规定。但它不是变量,不须要 后面加数据类型。标号只能用于多层嵌套循环中,不能在同级别的循环之间应用哦,那样的话,会让代码的执行程序乱套的!

当初,咱们就能够开始查找程旭元同学了。

剖析:

​ 1. 先应用 for 循环遍历每一个班级,定义标号:

label_class: for () {}

​ 2. 在班级循环体中,再应用 for 循环遍历每个同学

​ 3. 判断:如果班级编号为 3,同学编号为 10,则进行查找:break label_class;

示例代码:

public class Test{public static void main(String[] args) {
        label_class: // 外层循环标号
        //     1. 先应用 for 循环遍历每一个班级
        for (int classNumber = 1; classNumber <= 3; classNumber++) {
            // 2. 在班级循环体中,再应用 for 循环遍历每个同学
            for (int studentNumber = 1; studentNumber <= 15; studentNumber++) {
                // 打印正在查找的同学编号
                System.out.println("正在查找的第" + classNumber + "个班级的第" + studentNumber + "位同学"); 
                // 3. 判断:如果班级编号为 3,同学编号为 10,则进行查找
                if (classNumber == 3 && studentNumber == 10) {System.out.println("哈哈, 找到程旭元同学了, 整个循环完结");
                    break label_class; // 进行查找:完结指定标号的循环
                }
            }
        }
    }
}

输入:

正在查找的第 1 个班级的第 1 位同学
正在查找的第 1 个班级的第 2 位同学
正在查找的第 1 个班级的第 3 位同学
...
正在查找的第 1 个班级的第 15 位同学
正在查找的第 2 个班级的第 1 位同学
正在查找的第 2 个班级的第 2 位同学
...
正在查找的第 2 个班级的第 14 位同学
正在查找的第 2 个班级的第 15 位同学
正在查找的第 3 个班级的第 1 位同学
...
正在查找的第 3 个班级的第 9 位同学
正在查找的第 3 个班级的第 10 位同学
哈哈, 找到程旭元同学了, 整个循环完结

应用 break 标号; 语句完结指定名称的循环十分不便。此时,标号为 label_class 的外层循环被强制完结,那内层循环呢,还执行吗?很显著,内层循环也完结了,因为内层循环的代码不可能脱离外层循环独立执行啊。

再看另一种状况,什么样的场景下会应用 continue 标号; 语句。

需要:按批次检测商品的次品量。现有 3 个批次,每个批次有 10 件商品,如果某批次商品中蕴含任意一个次品,则该批次商品不合格,跳过该批次残余商品的检测并记录,持续下个批次。假如查找到第 2 个批次的第 5 件商品为次品。

剖析:

  1. 先应用 for 循环遍历每一个批次,定义标号:
    label_batch: for () {}

    1. 在批次循环体中,再应用 for 循环遍历每个商品
    2. 判断如果批次编号为 2,商品编号为 5,则完结以后批次的检测,持续下个批次:
      continue label_batch;

示例代码:

public class Test{public static void main(String[] args) {
        label_batch: // 外层循环标号
        // 1. 先应用 for 循环遍历每一个批次
        for (int batchNumber = 1; batchNumber <= 3; batchNumber++) {
            // 2. 在批次循环体中,再应用 for 循环遍历每个商品
            for (int goodsNumber = 1; goodsNumber <= 10; goodsNumber++) {
                // 打印正在检测的商品编号
                System.out.println("正在检测第" + batchNumber + "个批次的第" + goodsNumber + "件商品");
                // 3. 判断:如果批次编号为 2,商品编号为 5,则进行查找
                if (batchNumber == 2 && goodsNumber == 5) {System.err.println("记录:第" + batchNumber + "个批次的第" + goodsNumber + "件商品是次品");
                    continue label_batch; // 进行检测以后批次,持续下个批次:持续指定标号的循环
                }
            }
        }
    }
}

输入后果:

正在检测第 1 个批次的第 1 件商品
正在检测第 1 个批次的第 2 件商品
...
正在检测第 2 个批次的第 4 件商品
正在检测第 2 个批次的第 5 件商品
记录:第 2 个批次的第 5 件商品是次品
正在检测第 3 个批次的第 1 件商品
正在检测第 3 个批次的第 2 件商品
...
正在检测第 3 个批次的第 9 件商品
正在检测第 3 个批次的第 10 件商品

肯定要留神的是,break 标号;continue 标号; 两个语句含意的不同:break 代表“完结全副”,而 continue 代表“完结局部,而后持续”。认真辨别它们的含意,能力正确地应用它们。

3.3 循环案例:1024 程序员节,小黑带你发橙子

3.3.1 需要:

    *1024 程序员节 *,是传智播客发动的中国程序员独特的节日。每到 10 月 24 日,小黑都会按班级给每位同学发橙子。假如有 3 个班级,每个班级有 35 个同学,当初要将 100 个橙子别离发放给每位同学,每人只能拿一个。条件:如果该同学曾经有了橙子,则不再发给该同学;如果橙子发完了,则发放流动终止。

3.3.2 剖析:

    A:模仿发橙子的过程:循环每一个班级,而后遍历班级的每个同学,所以须要双层循环

​ B:假如编号为 5 的倍数的同学都曾经有了橙子,则发放到该同学时,应用 continue 语句完结 该次循环

​ C:橙子的数量为 0 时,应用 break + 标号; 语句完结外层循环,发放流动终止。

3.3.3 步骤:

​ A:实现给每位同学发橙子的性能

​ B:增加判断条件:跳过编号为 5 的倍数的同学

​ C:增加判断条件:橙子数目为 0,则终止发放

3.3.4 技术点:

​ 循环嵌套
​ break
​ continue
​ 标号

参考代码:

public class Test{public static void main(String[] args) {
        int orangeCount = 100; // 橙子总数 100 个
        label_class: // 外层循环标号:班级循环
        //     1. 应用 for 循环遍历每一个班级
        for (int classNumber = 1; classNumber <= 3; classNumber++) {
            // 2. 在班级循环体中,再应用 for 循环遍历每个同学
            for (int studentNumber = 1; studentNumber <= 35; studentNumber++) {if (orangeCount <= 0) { // 查看橙子数量是否足够,如果橙子数量不够了
                    break label_class; // 完结发橙子的流动:完结外层循环
                }
                if (studentNumber % 5 == 0) { // 查看以后同学编号:编号为 5 的倍数,该同学曾经有了橙子
                    continue; // 跳过以后同学
                }
                // 打印失去橙子的同学编号
                System.out.println("正在给第" + classNumber + "个班级的第" + studentNumber + "位同学发橙子"); 
                orangeCount--; // 橙子数量减 1
            }
        }
        // 流动完结,打印收回的橙子数量
        System.out.println("总共收回了" + (100 - orangeCount) + "个橙子");
    }
}

课程总结

  1. 三种循环的根本格局和执行流程(参考上文中图):

         for (初始化语句; 判断条件; 管制条件) {循环体}
        初始化语句;
        while (判断条件) {
            循环体;
            管制条件;
        }
        初始化语句;
        do {
            循环体;
            管制条件;
        } while (判断条件); 
  2. 循环的终止:

    break:完结以后循环

    continue:完结本次循环,持续下次循环

    循环嵌套:多组数据之间有蕴含关系时,应用循环嵌套格局:每层循环所代表的含意;变量名反复问题;

    标号:循环的名字,配合 breakcontinue 语句应用。

退出移动版