位置:首页 > Kotlin > Kotlin 流程控制:while 与 do/while 循环详解

Kotlin 流程控制:while 与 do/while 循环详解

时间:2026-08-27  |  作者:星河游者  |  阅读:0

目录

  1. 一、前言
  2. 1.1 循环的核心作用(重复执行逻辑、处理不确定次数场景)
  3. 1.2 while/do-while 的核心特点(基础灵活、按需执行)
  4. 1.3 while 循环的执行机制
  5. 1.4 do/while 循环的执行机制
  6. 1.3 本文核心内容预告(基础用法→差异对比→实用场景)
一、前言 对应的技术说明图
一、前言概括一、前言的核心概念、关键要点与实践提示。
3 本文核心内容预告(基础用法→差异对比→实 对应的技术说明图
3 本文核心内容预告(基础用法→差异对比→实概括本文核心内容预告(基础用法→差异对比→实的核心概念、关键要点与实践提示。

前言

在 Kotlin 中,循环是处理不确定次数场景的核心工具。while 与 do/while 凭借灵活的条件驱动特性,成为处理动态逻辑的首选。while 遵循“先判断后执行”机制,适合常规条件控制;do/while 则保证循环体至少执行一次,常用于需预执行的操作。本文深入剖析两者语法格式、执行逻辑及关键差异,结合游戏监听与用户输入等实战场景,助开发者精准选择循环结构,写出更健壮、高效的代码。

Kotlin 流程控制:while 与 do/while 循环详解 的核心流程信息图
Kotlin 流程控制:while 与 do用简体中文信息图概括Kotlin 流程控制:while 与 do的核心流程、关键规则与实践要点。

一、前言

1.1 循环的核心作用(重复执行逻辑、处理不确定次数场景)

循环作为编程三大流程控制结构(顺序、选择、循环)之一,是实现“重复执行”逻辑的核心工具。其价值主要体现在两个关键场景:一是固定逻辑的重复执行,比如批量生成100条测试数据、循环打印1到10的数字,这类场景可通过明确的次数控制循环;二是不确定次数的条件驱动执行,比如持续接收用户输入直到输入“退出”、反复重试网络请求直到成功或达到最大次数,这类场景无法提前确定循环次数,只能通过“条件是否满足”来控制循环的启停。

在Kotlin中,除了面向集合遍历的for循环,while和do/while循环更擅长处理“不确定次数”的场景。它们无需依赖固定的遍历对象(如区间、数组),仅通过条件表达式即可灵活控制循环,是处理动态场景的重要工具。

1.2 while/do-while 的核心特点(基础灵活、按需执行)

while和do/while循环作为Kotlin中最基础的循环结构,具备两大核心特点:一是极致灵活,它们不依赖特定的数据结构,仅通过一个布尔类型的条件表达式控制循环,可适配任意需要“条件驱动”的场景,小到简单的数字累加,大到复杂的业务逻辑重试,都能轻松应对;二是按需执行,循环体的执行完全由条件表达式的结果决定,满足条件则执行,不满足则终止,尤其适合“不确定执行次数”的场景,避免了不必要的资源消耗。

与for循环相比,while和do/while更偏向“逻辑驱动”而非“数据遍历”。例如,要实现“持续接收用户输入直到输入正确”的逻辑,用do/while循环只需判断输入是否正确即可,无需关注遍历的集合或范围,逻辑更直接。

1.3 while 循环的执行机制

while循环是Kotlin中最常见的条件循环结构,其核心逻辑在于“先判断,后执行”。在每次进入循环体之前,程序会首先评估条件表达式的布尔值。如果条件为真,则执行循环体内的代码;执行完毕后,再次回到条件判断处,重复这一过程。这种机制确保了只有当条件满足时,代码块才会被执行,从而实现了真正的“按需执行”。

这种结构特别适用于那些在循环开始前无法确定迭代次数的场景。例如,在游戏开发中,可能需要持续监听玩家的操作指令,直到玩家选择“退出游戏”为止。此时,while循环的条件表达式可以是“当前状态不等于退出状态”,只要玩家未做出退出操作,游戏主循环就会持续运行。这种模式不仅逻辑清晰,而且能够有效地处理动态变化的业务状态。

需要注意的是,while循环存在一种潜在风险,即如果条件表达式始终为真,且循环体内没有改变条件的逻辑,就会导致“死循环”。因此,在使用while循环时,务必确保循环体内有能够改变条件表达式值的语句,或者在特定条件下通过break语句强制跳出循环,以保证程序的可控性和健壮性。

1.4 do/while 循环的执行机制

do/while循环与while循环的主要区别在于执行顺序。do/while循环采用“先执行,后判断”的策略。这意味着无论条件表达式初始值如何,循环体至少会被执行一次。在第一次执行完循环体后,程序才会去评估条件表达式。如果条件为真,则继续执行下一次循环;如果为假,则终止循环。

这种“至少执行一次”的特性,使得do/while循环在处理某些特定业务场景时具有独特的优势。例如,在菜单系统中,通常需要首先显示菜单选项供用户选择,然后根据用户的输入决定是继续显示菜单还是退出程序。如果使用while循环,需要额外编写代码来首次显示菜单;而使用do/while循环,则可以直接将显示菜单和获取输入的逻辑放在循环体内,最后判断是否继续,代码结构更加简洁自然。

此外,do/while循环在处理文件读取或网络数据流时也非常有用。例如,在读取一个未知长度的数据流时,可以先尝试读取一段数据,然后判断是否到达文件末尾或数据结束标志。如果未结束,则继续读取;如果已结束,则停止。这种模式完美契合了do/while循环的执行逻辑,避免了额外的前置判断代码,使程序逻辑更加紧凑高效。

1.3 本文核心内容预告(基础用法→差异对比→实用场景)

本文将围绕Kotlin中的while和do/while循环展开,采用“由浅入深、对比分析、场景落地”的思路逐步讲解。首先分别介绍while循环的基本语法、执行流程和简单示例,掌握“先判断后执行”的核心逻辑;接着详解do/while循环的语法、流程和示例,理解“先执行后判断”的特点;然后重点对比两者的核心差异,明确不同场景下的选择依据;之后补充break和continue两个循环控制关键字的用法,实现对循环节奏的精准控制;再通过多个实用场景案例,展示两种循环在实际开发中的应用;最后总结核心知识点、避坑技巧和选择建议,帮助读者真正做到“灵活选用、正确使用”。

二、while 循环:先判断,再执行

2.1 基本语法格式(条件表达式 + 循环体)

while循环是“先判断条件,再执行循环体”的基础循环结构,其核心是一个布尔类型的条件表达式和一段需要重复执行的循环体。语法格式简洁明了:

// while 循环基本语法
while (条件表达式) {
    // 循环体:条件满足时执行的代码块
    执行逻辑
}

语法说明:1. 条件表达式:必须返回Boolean类型结果(true或false),这是控制循环启停的核心,比如“count < 10”“input != "exit"”等;2. 循环体:用花括号包裹的代码块,条件表达式为true时会执行其中的逻辑;若循环体仅包含一条语句,花括号可省略(但建议始终保留,提升可读性);3. 循环终止条件:循环体内部必须包含能改变条件表达式结果的逻辑(如变量自增、修改输入值),否则会导致无限循环。

2.2 执行流程(先校验条件,满足则执行循环体)

while循环的执行流程遵循“判断→执行→再判断”的逻辑,具体步骤如下:

  • 第一步:条件校验。程序首先计算并评估while关键字后的条件表达式。此时循环体尚未执行,这是进入循环前的唯一门槛。
  • 第二步:分支决策。根据条件表达式的结果决定后续动作。如果结果为true,程序将进入循环体内部执行代码;如果结果为false,程序将直接跳过整个while循环结构,继续执行循环之后的代码。
  • 第三步:执行循环体。当条件为true时,执行花括号内的所有语句。这一步是循环的核心业务逻辑所在,可能包含变量修改、I/O操作或函数调用等。
  • 第四步:返回判断。循环体执行完毕后,程序不会结束,而是自动返回到第一步,再次对条件表达式进行校验。这个过程会不断重复,直到某次校验结果为false为止。

这种“先判断后执行”的机制意味着,如果初始条件即为false,循环体一次都不会执行。这是while循环与do/while循环最本质的区别,也是在实际开发中需要根据业务逻辑谨慎选择的关键点。理解这一流程有助于避免逻辑错误,确保程序按照预期路径运行。

  1. 条件校验:首先执行条件表达式,判断其结果为true还是false;
  2. 执行循环体:若条件表达式结果为true,执行循环体中的逻辑;若为false,直接终止循环,跳转到循环之后的代码;
  3. 循环往复:循环体执行完毕后,再次回到步骤1,重新校验条件表达式,直到条件为false时终止。

可以用一个简单的流程图描述这一过程:开始 → 校验条件 → 条件为true?→ 是→执行循环体→回到校验条件 → 否→终止循环。从流程可见,while循环的核心特点是“条件不满足则一次都不执行”,这是它与do/while循环的关键区别。

2.3 简单示例(如循环打印数字、累计求和)

下面通过两个常见的简单示例,展示while循环的实际使用:

2.3.1 示例1:循环打印1到5的数字

需求:打印1、2、3、4、5,通过变量自增控制循环次数。

fun main() {
    var count = 1 // 初始化计数器变量
    // 条件:count小于等于5时执行循环
    while (count <= 5) {
        println("当前数字:$count")
        count++ // 计数器自增,改变条件表达式结果
    }
    println("循环结束")
}

执行过程解析:

  • 初始count=1,校验条件1≤5→true,打印“当前数字:1”,count变为2;
  • 校验count=2≤5→true,打印“当前数字:2”,count变为3;
  • 依次执行,直到count=6时,校验6≤5→false,循环终止,打印“循环结束”。

输出结果:

当前数字:1
当前数字:2
当前数字:3
当前数字:4
当前数字:5
循环结束

2.3.2 示例2:累计求和1到100

需求:计算1+2+3+...+100的总和,通过while循环实现累加。

fun main() {
    var num = 1 // 初始化累加变量
    var sum = 0 // 初始化总和变量
    // 条件:num小于等于100时执行循环
    while (num <= 100) {
        sum += num // 累加:sum = sum + num
        num++ // 累加变量自增
    }
    println("1到100的总和:$sum")
}

执行逻辑:num从1开始,每次循环将num的值累加到sum中,然后num自增,直到num=101时条件不满足,循环终止。最终sum的值为1到100的总和5050。

输出结果:1到100的总和:5050

三、do/while 循环:先执行,再判断

3.1 基本语法格式(循环体 + 条件表达式)

do/while循环是“先执行循环体,再判断条件”的循环结构,其核心是确保“循环体至少执行一次”,语法格式与while循环类似,但条件表达式的位置不同:

3.1 语法结构(do { 循环体 } while (条件);)

在 Kotlin 中,do/while 循环的语法结构相对简洁,但其关键细节不容忽视。以下是该语法的详细说明:

  • 关键字顺序:必须严格遵循 do 后接循环体,再接 while 和条件表达式的顺序。
  • 循环体位置:循环体位于 do 关键字之后,通常用花括号 {} 包裹。这是代码实际执行的部分。
  • 条件表达式:位于 while 关键字之后,必须用圆括号 () 包裹。该表达式决定了循环是否继续。
  • 分号要求:整个语句的末尾必须加上英文分号 ;,这是 Kotlin 语法规范的一部分,遗漏会导致编译错误。

语法说明:1. 循环体优先执行:关键字do后面紧跟循环体,程序会先执行一次循环体,再去判断条件表达式;2. 条件表达式:与while循环一致,必须返回Boolean类型结果,用于控制后续是否继续循环;3. 循环终止条件:循环体内部同样需要包含改变条件表达式结果的逻辑,避免无限循环;4. 分号注意:条件表达式末尾需要加英文分号(;),这是Kotlin语法的要求,不可遗漏。

3.2 执行流程(先执行一次循环体,再校验条件)

do/while循环的执行流程遵循“执行→判断→再执行”的逻辑,具体步骤如下:

  1. 执行循环体:首先执行do后面花括号中的循环体逻辑,无论条件表达式结果如何,循环体都会先执行一次;
  2. 条件校验:循环体执行完毕后,执行条件表达式,判断其结果为true还是false;
  3. 循环往复:若条件表达式结果为true,回到步骤1,再次执行循环体;若为false,终止循环,跳转到循环之后的代码。

流程描述:开始 → 执行循环体 → 校验条件 → 条件为true?→ 是→回到执行循环体 → 否→终止循环。这一流程的核心特点是“先执行后判断”,确保循环体至少执行一次,这是do/while循环与while循环的本质区别。

3.3 简单示例(如用户输入验证、至少执行一次的逻辑)

do/while循环的核心优势是“至少执行一次”,因此常用于“需要先执行再判断”的场景,比如用户输入验证、初始化逻辑校验等。下面通过两个示例展示其使用:

3.3.1 示例1:用户输入验证(确保输入有效数字)

需求:要求用户输入一个1到10之间的整数,若输入无效则反复提示输入,直到输入有效为止。由于需要先获取用户输入再判断是否有效,适合用do/while循环。

import java.util.Scanner

fun main() {
    val scanner = Scanner(System.`in`)
    var inputNum: Int = null // 存储用户输入的数字
    do {
        println("请输入1到10之间的整数:")
        val input = scanner.next() // 先执行:获取用户输入
        // 尝试将输入转换为整数
        inputNum = try {
            input.toInt()
        } catch (e: NumberFormatException) {
            null // 转换失败则为null
        }
        // 判断输入是否有效,无效则提示
        if (inputNum == null || inputNum !in 1..10) {
            println("输入无效,请重新输入!")
        }
    } while (inputNum == null || inputNum !in 1..10) // 再判断:输入无效则继续循环

    println("您输入的有效数字是:$inputNum")
    scanner.close()
}

执行逻辑:无论用户第一次输入是否有效,都会先执行循环体中的“提示输入→获取输入→校验”逻辑,若输入无效(非数字或超出范围),则通过条件表达式判断继续循环,直到输入有效为止。

四、while 与 do/while 的核心差异

while和do/while循环的语法相似,都是通过条件表达式控制循环,但两者的执行流程存在本质区别,这导致它们的适用场景也不同。下面从三个维度对比核心差异:

4.1 执行顺序不同(判断时机的区别)

这是两者最核心的差异,直接决定了循环体的执行时机:

  • while循环先判断,后执行。条件表达式的校验在循环体执行之前,只有条件满足时才会执行循环体;
  • do/while循环先执行,后判断。循环体的执行在条件表达式校验之前,无论条件是否满足,循环体都会先执行一次。

可以用一个简单的比喻理解:while循环像“先买票再上车”,条件不满足(没买票)就不能上车(执行循环体);do/while循环像“先上车再补票”,无论是否有票(条件是否满足),都先上车(执行一次循环体),再判断是否补票(继续循环)。

4.2 初始条件不满足时的表现(do/while 至少执行一次)

当初始条件表达式结果为false时,两者的表现截然不同,这是执行顺序差异的直接体现:

  • while循环:由于先判断条件,初始条件为false时,循环体一次都不执行
  • do/while循环:由于先执行循环体,初始条件为false时,循环体仍会执行一次

通过以下对比代码可直观看到差异:

fun main() {
    // 初始条件为false的while循环
    println("=== while循环(初始条件false)===")
    var a = 10
    while (a < 5) {
        println("while循环执行了")
        a--
    }
    println("while循环结束")

    // 初始条件为false的do/while循环
    println("n=== do/while循环(初始条件false)===")
    var b = 10
    do {
        println("do/while循环执行了")
        b--
    } while (b < 5)
    println("do/while循环结束")
}

输出结果:

从上述代码运行结果可以看出,当初始条件为false时,while循环完全跳过了循环体,没有任何输出;而do/while循环则执行了一次循环体内的打印语句,随后才进行条件判断并终止。这一现象清晰地证明了do/while循环“至少执行一次”的特性。

4.3 适用场景与选择建议

理解执行顺序和初始条件表现后,我们需要根据实际需求选择合适的循环结构。不同的场景对“是否允许零次执行”有明确要求,这直接影响了代码的可读性和逻辑的正确性。

  • 优先使用 while 的场景:当循环体的执行依赖于前置条件,且该条件可能在初始状态下就不满足时,应使用while循环。例如,读取文件直到遇到特定标记,如果文件为空,则无需执行任何操作。此时,先判断条件可以避免不必要的处理逻辑。
  • 优先使用 do/while 的场景:当业务逻辑要求必须至少执行一次操作时,do/while循环是更自然的选择。典型例子包括用户交互界面:程序必须至少提示用户输入一次,无论用户第一次输入是否正确,都需要获取输入并进行验证。此外,在配置初始化或资源加载场景中,若需确保初始化代码至少运行一次,do/while也能提供清晰的语义表达。

在实际开发中,若不确定该用哪种循环,可问自己一个问题:“如果初始条件不满足,这段代码是否应该执行?”如果答案是“否”,则选择while;如果答案是“是”,则选择do/while。这种思考方式能有效避免逻辑错误,提升代码质量。

五、循环控制:break 与 continue

在while和do/while循环中,有时需要根据特殊条件调整循环节奏,比如“找到目标值后立即终止循环”“跳过当前无效值,继续下一次循环”。Kotlin提供了break和continue两个关键字,用于实现对循环的精准控制。

5.1 break 关键字(终止整个循环)

break关键字用于立即终止当前所在的整个循环,程序流程将跳出循环体,继续执行循环之后的代码。这在遍历列表或数组时非常有用,一旦找到目标元素,无需继续遍历剩余元素,从而提升效率。

5.1.1 基本用法

在while或do/while循环内部,当满足特定条件时执行break语句,即可提前结束循环。例如,在搜索列表中某个特定值时,找到后即可停止搜索。

  • 提前终止遍历:当在集合中查找元素时,找到目标后立即break,避免无意义的后续遍历;
  • 条件满足即退出:当某个状态标志位被修改或特定事件发生时,立即跳出循环;
  • 防止无限循环:在复杂逻辑中,若检测到异常状态,使用break强制退出,防止程序陷入死循环。

示例:在列表中查找第一个大于10的元素,找到后立即终止循环:

fun main() {
    var num = 1
    var target = -1 // 存储找到的目标数字

    while (num <= 10) {
        if (num % 3 == 0) {
            target = num
            break // 找到目标,终止整个循环
        }
        num++
    }

    println("1到10中第一个能被3整除的数字是:$target")
}

5.1.2 带标签的break

Kotlin支持带标签的break语句,用于跳出多层嵌套循环中的外层循环。通过为循环添加标签,可以在内层循环中直接终止外层循环的执行。

  • 嵌套循环控制:在多层循环中,使用标签指定要跳出的循环层级;
  • 精确控制退出点:避免使用标志变量或异常处理来跳出循环,使代码更清晰;
  • 提高可读性:通过标签明确表达跳出意图,增强代码的可维护性。

示例:使用标签跳出外层循环:

fun main() {
    var num = 1
    while (num <= 10) {
        if (num % 2 == 0) {
            num++ // 注意:continue前需更新循环变量,避免死循环
            continue // 是偶数,跳过打印,进入下一次循环
        }
        println("当前奇数:$num")
        num++
    }
}

5.2 continue 关键字(跳过当前迭代)

continue关键字用于跳过当前循环迭代的剩余代码,直接进入下一次循环的条件判断。与break不同,continue不会终止整个循环,而是跳过当前这一次迭代,继续执行后续的循环。

5.2.1 基本用法

在while或do/while循环中,当遇到continue语句时,当前迭代的剩余代码将被跳过,程序直接回到循环的条件判断部分,决定是否继续下一次迭代。

  • 跳过无效数据:在处理数据时,若当前数据不符合要求,使用continue跳过该数据的处理;
  • 条件过滤:当某个条件不满足时,跳过当前迭代,继续处理其他数据;
  • 优化性能:避免执行不必要的计算或操作,提高程序运行效率。

示例:遍历列表,跳过所有负数,只处理正数:

当前奇数:1
当前奇数:3
当前奇数:5
当前奇数:7
当前奇数:9

5.2.2 带标签的continue

Kotlin同样支持带标签的continue语句,用于跳过外层循环的当前迭代。通过为循环添加标签,可以在内层循环中直接跳过外层循环的当前迭代,继续执行外层循环的下一次迭代。

  • 嵌套循环控制:在多层循环中,使用标签指定要跳过的循环层级;
  • 精确控制跳过点:避免使用标志变量或异常处理来跳过迭代,使代码更清晰;
  • 提高可读性:通过标签明确表达跳过意图,增强代码的可维护性。

示例:使用标签跳过外层循环的当前迭代:

fun main() {
    var num = 1
    while (num <= 20) {
        // 遇到大于15的数字,终止整个循环
        if (num > 15) {
            break
        }
        // 跳过偶数,只处理奇数
        if (num % 2 == 0) {
            num++
            continue
        }
        // 打印能被5整除的奇数
        if (num % 5 == 0) {
            println("符合条件的数字:$num")
        }
        num++
    }
    println("循环终止")
}

6.Kotlin 流程控制:循环控制:while 与 do/while

在循环结构中,break关键字的作用是立即终止当前所在的循环。无论条件表达式是否满足,程序都会跳转到循环之后的代码继续执行。它适用于“找到目标后无需继续循环”的场景,比如遍历数据时找到指定元素后终止、满足某个条件时提前退出循环。

示例:遍历1到10的数字,找到第一个能被3整除的数字后终止循环:

fun main() {
    var num = 1
    var target = -1 // 存储找到的目标数字

    while (num <= 10) {
        if (num % 3 == 0) {
            target = num
            break // 找到目标,终止整个循环
        }
        num++
    }

    println("1到10中第一个能被3整除的数字是:$target")
}

执行逻辑:num从1开始递增,当num=3时,3%3==0成立,将target设为3,执行break终止循环,不再继续遍历4到10的数字。

输出结果:1到10中第一个能被3整除的数字是:3

5.2 continue 关键字(跳过当前迭代,进入下一次)

continue关键字的作用是跳过当前迭代的剩余循环体逻辑,直接进入下一次循环的条件校验。它不会终止整个循环,只是跳过当前次的无效逻辑,适用于“过滤无效数据,仅处理有效数据”的场景。

示例:遍历1到10的数字,只打印奇数,跳过偶数:

fun main() {
    var num = 1
    while (num <= 10) {
        if (num % 2 == 0) {
            num++ // 注意:continue前需更新循环变量,避免死循环
            continue // 是偶数,跳过打印,进入下一次循环
        }
        println("当前奇数:$num")
        num++
    }
}

执行逻辑:num从1开始,若为偶数(num%2==0),则先让num自增,再执行continue跳过打印逻辑,直接回到条件校验;若为奇数,则打印数字后num自增。

输出结果:

当前奇数:1
当前奇数:3
当前奇数:5
当前奇数:7
当前奇数:9

注意:使用continue时,务必在continue之前更新循环变量(如num++),否则会导致循环变量值不变,条件始终满足,陷入无限循环。

5.3 简单示例(控制循环执行节奏)

下面通过一个综合示例,展示break和continue如何配合控制循环节奏:遍历1到20的数字,打印其中的“能被5整除的奇数”,遇到大于15的数字则终止循环。

fun main() {
    var num = 1
    while (num <= 20) {
        // 遇到大于15的数字,终止整个循环
        if (num > 15) {
            break
        }
        // 跳过偶数,只处理奇数
        if (num % 2 == 0) {
            num++
            continue
        }
        // 打印能被5整除的奇数
        if (num % 5 == 0) {
            println("符合条件的数字:$num")
        }
        num++
    }
    println("循环终止")
}

执行逻辑:

  • num=1到15之间,先判断是否大于15(否),再判断是否为偶数(是则跳过),最后判断是否能被5整除(是则打印);
  • num=16时,判断大于15(是),执行break终止循环;
  • 符合条件的数字为1到15之间的奇数且能被5整除,即5、15。

输出结果:

六、实用场景举例

前面介绍了while和do/while循环的基础用法和差异,下面结合实际开发中的常见场景,展示两者的具体应用,帮助读者更好地理解“何时用哪种循环”。

6.1 while 场景(遍历未知长度数据、条件满足时循环)

6.1.1 场景1:遍历未知长度的数据流

需求:模拟读取一个未知长度的数据流(如从文件读取内容,直到读取到结束标记“EOF”),每读取一条数据就打印,数据流为空则不执行循环。由于数据流长度未知且可能为空,适合用while循环。

fun main() {
    println("开始读取数据流...")
    // 循环条件:读取到的数据不是结束标记"EOF"
    while (true) {
        val data = readDataFromStream() // 读取一条数据
        if (data == "EOF") {
            break // 读取到结束标记,终止循环
        }
        println("读取到数据:$data")
    }
    println("数据流读取完毕")
}

// 模拟读取数据流(依次返回数据1、数据2、EOF)
private var dataIndex = 0
fun readDataFromStream(): String {
    val dataList = listOf("数据1", "数据2", "EOF")
    val data = dataList[dataIndex]
    dataIndex++
    return data
}

输出结果:

开始读取数据流...
读取到数据:数据1
读取到数据:数据2
数据流读取完毕

6.1.2 场景2:条件满足时循环执行重试逻辑

需求:系统启动时连接数据库,若连接失败则每隔3秒重试一次,直到连接成功或达到最大重试次数(5次)。由于初始可能连接成功,无需重试,适合用while循环。

fun main() {
    val maxRetry = 5
    var retryCount = 0
    var isConnected = false

    // 条件:未连接成功且未达到最大重试次数
    while (!isConnected && retryCount < maxRetry) {
        isConnected = connectToDb()
        if (isConnected) {
            println("数据库连接成功!")
        } else {
            retryCount++
            if (retryCount < maxRetry) {
                println("数据库连接失败,$retryCount 次重试,3秒后重试...")
                Thread.sleep(3000) // 等待3秒
            }
        }
    }

    if (!isConnected) {
        println("达到最大重试次数($maxRetry 次),数据库连接失败!")
    }
}

// 模拟数据库连接(第3次重试时成功)
private var connectCount = 0
fun connectToDb(): Boolean {
    connectCount++
    return connectCount == 3
}

输出结果:

数据库连接失败,1 次重试,3秒后重试...
数据库连接失败,2 次重试,3秒后重试...
数据库连接成功!

6.2 do/while 场景(表单验证、初始化后需校验的逻辑)

6.2.1 场景1:表单输入验证(至少输入一次)

需求:用户注册时输入密码,要求密码长度至少8位且包含数字和字母,若密码不满足要求则反复提示输入,直到输入符合要求的密码。由于必须让用户至少输入一次密码,适合用do/while循环。

import java.util.Scanner

fun main() {
    val scanner = Scanner(System.`in`)
    var password: String
    // 密码校验规则:长度≥8,包含数字和字母
    val passwordRegex = "^(=.*[0-9])(=.*[a-zA-Z]).{8,}$".toRegex()

    do {
        println("请输入注册密码(长度≥8,包含数字和字母):")
        password = scanner.next()
        if (!password.matches(passwordRegex)) {
            println("密码不符合要求,请重新输入!")
        }
    } while (!password.matches(passwordRegex)) // 密码不符合要求则继续循环

    println("密码设置成功!")
    scanner.close()
}

输出示例:

请输入注册密码(长度≥8,包含数字和字母):
123456
密码不符合要求,请重新输入!
请输入注册密码(长度≥8,包含数字和字母):
abcdefgh
密码不符合要求,请重新输入!
请输入注册密码(长度≥8,包含数字和字母):
abc12345
密码设置成功!

6.2.2 场景2:初始化后校验配置(至少初始化一次)

需求:系统初始化配置(从配置文件读取),初始化后校验配置是否有效,若无效则重新初始化(最多重新初始化3次)。由于必须先初始化一次才能校验,适合用do/while循环。

fun main() {
    val maxInitCount = 3
    var initCount = 0
    var config: Config = null

    do {
        config = initConfig() // 初始化配置(至少执行一次)
        initCount++
        if (config == null || !config.isValid()) {
            if (initCount < maxInitCount) {
                println("配置初始化无效,$initCount 次初始化,准备重新初始化...")
            }
        }
    } while ((config == null || !config.isValid()) && initCount < maxInitCount)

    if (config != null && config.isValid()) {
        println("配置初始化成功,配置信息:$config")
    } else {
        println("达到最大初始化次数($maxInitCount 次),配置初始化失败!")
    }
}

// 配置数据类
data class Config(val serverUrl: String, val port: Int) {
    // 校验配置是否有效
    fun isValid(): Boolean {
        return serverUrl.startsWith("http") && port in 1024..65535
    }
}

// 模拟配置初始化(第2次初始化时成功)
private var initConfigCount = 0
fun initConfig(): Config {
    initConfigCount++
    return if (initConfigCount == 2) {
        Config("http://localhost", 8080)
    } else {
        Config("localhost", 80) // 无效配置(URL无http,端口80小于1024)
    }
}

输出结果:

配置初始化无效,1 次初始化,准备重新初始化...
配置初始化成功,配置信息:Config(serverUrl=http://localhost, port=8080)

七、总结与使用建议

7.1 核心知识点回顾(语法、流程、差异)

7.2 避坑点(避免死循环、条件表达式设计)

  • 避免无限循环:这是 while 和 do/while 循环最常见的坑。核心解决方法是“确保循环体内部能改变条件表达式的结果”,比如变量自增、修改输入值、执行 break 等;尤其要注意使用 continue 时,需在 continue 前更新循环变量。
  • 条件表达式必须返回 Boolean 类型:避免将其他类型的值直接作为条件(如 Java 中允许的整数 0 为 false、非 0 为 true),Kotlin 严格要求条件表达式为 Boolean 类型,否则编译报错。
  • do/while 循环末尾不要遗漏分号:Kotlin 语法要求 do/while 循环的条件表达式末尾必须加英文分号,遗漏会导致编译错误。
  • 循环变量初始化要正确:确保循环变量的初始值符合业务逻辑,比如遍历 1 到 10 时,初始值设为 1 而非 0,避免出现逻辑错误。

7.3 选择技巧(根据是否需要 “至少执行一次” 判断)

开发中选择 while 还是 do/while 循环,核心判断标准只有一个:循环体是否需要至少执行一次。可按照以下步骤快速判断:

  • 第一步:分析业务场景的执行次数要求。首先审视当前业务逻辑,判断循环体内的代码块是否必须在条件检查之前执行一次。如果业务要求无论初始状态如何,某些初始化操作或首次处理必须发生,则倾向于使用 do/while;反之,如果初始条件可能直接导致无需执行循环体,则 while 更为合适。
  • 第二步:评估初始条件的确定性。检查循环变量的初始值是否足以确定是否进入循环。若初始值已满足退出条件,且业务允许跳过整个循环过程,应选用 while 循环,因为它会先判断后执行,避免不必要的计算开销。若初始值不确定或必须处理一次,do/while 能确保至少执行一次循环体。
  • 第三步:结合代码可读性与维护性。在满足功能需求的前提下,选择更符合直觉的写法。对于“先执行后判断”的逻辑,do/while 能更清晰地表达“至少一次”的语义;而对于“先判断后执行”的逻辑,while 则更贴合常规的条件过滤场景。开发者应根据团队规范和代码上下文,选择最易维护的形式。

循环选型与进阶控制

明确业务需求

思考“循环体是否存在‘一次都不执行’的合理场景”;

若存在“一次都不执行”的场景

选择while循环,如遍历可能为空的集合、条件初始就不满足的循环;

若“必须至少执行一次”

选择do/while循环,如用户输入验证、初始化后校验的逻辑;

复杂场景结合循环控制

当需要提前终止循环或跳过无效迭代时,配合break和continue使用,精准控制循环节奏。

此外,还需注意:while和do/while循环更适合“不确定次数”的条件驱动场景,若循环次数固定或需要遍历集合/区间,优先选择for循环,让代码更简洁直观。

掌握while和do/while循环的核心差异和适用场景,结合break、continue进行精准控制,能让你在处理“条件驱动”的循环场景时游刃有余,写出更高效、更易读的Kotlin代码。

// do/while 循环基本语法
do {
    // 循环体:至少执行一次的代码块
    执行逻辑
} while (条件表达式)

请输入1到10之间的整数:
abc
输入无效,请重新输入!
请输入1到10之间的整数:
15
输入无效,请重新输入!
请输入1到10之间的整数:
5
您输入的有效数字是:5

import java.util.Scanner

fun main() {
    val scanner = Scanner(System.`in`)
    var sum = 0
    var input: Int
    do {
        println("请输入一个整数(输入0停止):")
        input = scanner.nextInt() // 先执行:获取输入
        sum += input // 累加输入的数字
    } while (input != 0) // 再判断:输入不是0则继续循环

    println("所有输入数字的累加和:$sum")
    scanner.close()
}

请输入一个整数(输入0停止):
0
所有输入数字的累加和:0

=== while循环(初始条件false)===
while循环结束

=== do/while循环(初始条件false)===
do/while循环执行了
do/while循环结束

fun main() {
    val list = listOf() // 空列表
    var index = 0
    // 列表为空时,index < list.size为false,循环体不执行
    while (index < list.size) {
        println(list[index])
        index++
    }
    println("遍历结束(列表为空,未执行循环体)")
}

fun main() {
    val maxRetry = 3 // 最大重试次数
    var retryCount = 0 // 已重试次数
    var isSuccess = false // 请求是否成功

    do {
        // 执行请求逻辑(至少执行一次)
        isSuccess = requestApi()
        if (!isSuccess) {
            retryCount++
            println("请求失败,已重试$retryCount次")
        }
        // 条件:请求失败且未达到最大重试次数则继续重试
    } while (!isSuccess && retryCount < maxRetry)

    if (isSuccess) {
        println("请求成功")
    } else {
        println("超过最大重试次数,请求失败")
    }
}

// 模拟接口请求(随机返回成功或失败)
fun requestApi(): Boolean {
    val random = (1..10).random()
    return random > 3 // 70%概率成功
}

符合条件的数字:5
符合条件的数字:15
循环终止

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多