希望帮你在Kotlin进阶路上少走弯路,在技术上稳步提升。当然,由于个人知识储备有限,笔记中难免存在疏漏或表述不当的地方,也非常欢迎大家提出宝贵意见,一起交流进步。 ------ Android_小雨
整体目录:Kotlin 进阶不迷路:41 个核心知识点,构建完整知识体系
一、前言
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 本文核心内容预告(基础用法→差异对比→实用场景)
本文将围绕Kotlin中的while和do/while循环展开,采用"由浅入深、对比分析、场景落地"的思路逐步讲解。首先分别介绍while循环的基本语法、执行流程和简单示例,掌握"先判断后执行"的核心逻辑;接着详解do/while循环的语法、流程和示例,理解"先执行后判断"的特点;然后重点对比两者的核心差异,明确不同场景下的选择依据;之后补充break和continue两个循环控制关键字的用法,实现对循环节奏的精准控制;再通过多个实用场景案例,展示两种循环在实际开发中的应用;最后总结核心知识点、避坑技巧和选择建议,帮助读者真正做到"灵活选用、正确使用"。
二、while 循环:先判断,再执行
2.1 基本语法格式(条件表达式 + 循环体)
while循环是"先判断条件,再执行循环体"的基础循环结构,其核心是一个布尔类型的条件表达式和一段需要重复执行的循环体。语法格式简洁明了:
kotlin
// while 循环基本语法
while (条件表达式) {
// 循环体:条件满足时执行的代码块
执行逻辑
}
语法说明:1. 条件表达式 :必须返回Boolean类型结果(true或false),这是控制循环启停的核心,比如"count < 10""input != "exit""等;2. 循环体 :用花括号包裹的代码块,条件表达式为true时会执行其中的逻辑;若循环体仅包含一条语句,花括号可省略(但建议始终保留,提升可读性);3. 循环终止条件:循环体内部必须包含能改变条件表达式结果的逻辑(如变量自增、修改输入值),否则会导致无限循环。
2.2 执行流程(先校验条件,满足则执行循环体)
while循环的执行流程遵循"判断→执行→再判断"的逻辑,具体步骤如下:
- 条件校验:首先执行条件表达式,判断其结果为true还是false;
- 执行循环体:若条件表达式结果为true,执行循环体中的逻辑;若为false,直接终止循环,跳转到循环之后的代码;
- 循环往复:循环体执行完毕后,再次回到步骤1,重新校验条件表达式,直到条件为false时终止。
可以用一个简单的流程图描述这一过程:开始 → 校验条件 → 条件为true?→ 是→执行循环体→回到校验条件 → 否→终止循环。从流程可见,while循环的核心特点是"条件不满足则一次都不执行",这是它与do/while循环的关键区别。
2.3 简单示例(如循环打印数字、累计求和)
下面通过两个常见的简单示例,展示while循环的实际使用:
2.3.1 示例1:循环打印1到5的数字
需求:打印1、2、3、4、5,通过变量自增控制循环次数。
kotlin
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循环实现累加。
kotlin
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循环类似,但条件表达式的位置不同:
kotlin
// do/while 循环基本语法
do {
// 循环体:至少执行一次的代码块
执行逻辑
} while (条件表达式)
语法说明:1. 循环体优先执行 :关键字do后面紧跟循环体,程序会先执行一次循环体,再去判断条件表达式;2. 条件表达式 :与while循环一致,必须返回Boolean类型结果,用于控制后续是否继续循环;3. 循环终止条件 :循环体内部同样需要包含改变条件表达式结果的逻辑,避免无限循环;4. 分号注意:条件表达式末尾需要加英文分号(;),这是Kotlin语法的要求,不可遗漏。
3.2 执行流程(先执行一次循环体,再校验条件)
do/while循环的执行流程遵循"执行→判断→再执行"的逻辑,具体步骤如下:
- 执行循环体:首先执行do后面花括号中的循环体逻辑,无论条件表达式结果如何,循环体都会先执行一次;
- 条件校验:循环体执行完毕后,执行条件表达式,判断其结果为true还是false;
- 循环往复:若条件表达式结果为true,回到步骤1,再次执行循环体;若为false,终止循环,跳转到循环之后的代码。
流程描述:开始 → 执行循环体 → 校验条件 → 条件为true?→ 是→回到执行循环体 → 否→终止循环。这一流程的核心特点是"先执行后判断",确保循环体至少执行一次,这是do/while循环与while循环的本质区别。
3.3 简单示例(如用户输入验证、至少执行一次的逻辑)
do/while循环的核心优势是"至少执行一次",因此常用于"需要先执行再判断"的场景,比如用户输入验证、初始化逻辑校验等。下面通过两个示例展示其使用:
3.3.1 示例1:用户输入验证(确保输入有效数字)
需求:要求用户输入一个1到10之间的整数,若输入无效则反复提示输入,直到输入有效为止。由于需要先获取用户输入再判断是否有效,适合用do/while循环。
kotlin
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()
}
执行逻辑:无论用户第一次输入是否有效,都会先执行循环体中的"提示输入→获取输入→校验"逻辑,若输入无效(非数字或超出范围),则通过条件表达式判断继续循环,直到输入有效为止。
输出示例(输入无效后再输入有效):
请输入1到10之间的整数:
abc
输入无效,请重新输入!
请输入1到10之间的整数:
15
输入无效,请重新输入!
请输入1到10之间的整数:
5
您输入的有效数字是:5
3.3.2 示例2:至少执行一次的累加逻辑
需求:让用户输入数字进行累加,输入0时停止,要求即使第一次就输入0,也要先记录输入再终止(即至少执行一次输入和判断)。
kotlin
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,循环体会先执行"提示→获取输入→累加",然后判断input=0,循环终止,累加和为0;若输入其他数字,则继续循环。这确保了"输入→累加"逻辑至少执行一次。
输出示例(第一次输入0):
请输入一个整数(输入0停止):
0
所有输入数字的累加和:0
四、while 与 do/while 的核心差异
while和do/while循环的语法相似,都是通过条件表达式控制循环,但两者的执行流程存在本质区别,这导致它们的适用场景也不同。下面从三个维度对比核心差异:
4.1 执行顺序不同(判断时机的区别)
这是两者最核心的差异,直接决定了循环体的执行时机:
- while循环 :先判断,后执行。条件表达式的校验在循环体执行之前,只有条件满足时才会执行循环体;
- do/while循环 :先执行,后判断。循环体的执行在条件表达式校验之前,无论条件是否满足,循环体都会先执行一次。
可以用一个简单的比喻理解:while循环像"先买票再上车",条件不满足(没买票)就不能上车(执行循环体);do/while循环像"先上车再补票",无论是否有票(条件是否满足),都先上车(执行一次循环体),再判断是否补票(继续循环)。
4.2 初始条件不满足时的表现(do/while 至少执行一次)
当初始条件表达式结果为false时,两者的表现截然不同,这是执行顺序差异的直接体现:
- while循环 :由于先判断条件,初始条件为false时,循环体一次都不执行;
- do/while循环 :由于先执行循环体,初始条件为false时,循环体仍会执行一次。
通过以下对比代码可直观看到差异:
kotlin
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循环结束")
}
输出结果:
arduino
=== while循环(初始条件false)===
while循环结束
=== do/while循环(初始条件false)===
do/while循环执行了
do/while循环结束
结果显示:while循环因初始条件a=10<5为false,循环体一次都没执行;而do/while循环虽初始条件b=10<5为false,但循环体仍执行了一次。
4.3 适用场景对比(何时选 while,何时选 do/while)
执行顺序和初始条件表现的差异,决定了两者的适用场景截然不同,开发中需根据"是否需要循环体至少执行一次"来选择:
4.3.1 while循环适用场景
当循环体"可能一次都不执行"时,优先选择while循环,典型场景包括:
- 不确定是否需要执行的遍历场景:如遍历一个可能为空的数据流,若数据流为空则循环体不执行;
- 条件驱动的动态循环场景:如"当温度低于0℃时启动加热",若初始温度就高于0℃,则加热逻辑一次都不执行;
- 固定次数但可能为0的循环场景:如"循环打印n个数字,n可能为0",若n=0则不打印。
示例:遍历一个可能为空的列表,打印其中的元素(列表为空则不执行):
kotlin
fun main() {
val list = listOf<String>() // 空列表
var index = 0
// 列表为空时,index < list.size为false,循环体不执行
while (index < list.size) {
println(list[index])
index++
}
println("遍历结束(列表为空,未执行循环体)")
}
4.3.2 do/while循环适用场景
当循环体"必须至少执行一次"时,优先选择do/while循环,典型场景包括:
- 用户输入验证场景:如"获取用户有效输入",必须先让用户输入一次,再判断是否有效;
- 初始化后校验场景:如"初始化一个配置后,校验配置是否有效,无效则重新初始化",初始化逻辑必须至少执行一次;
- 至少执行一次的业务逻辑场景:如"重试接口请求,最多重试3次",即使第一次请求就成功,也需要先执行一次请求逻辑。
示例:重试接口请求(最多重试3次,至少请求一次):
kotlin
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%概率成功
}
五、循环控制:break 与 continue
在while和do/while循环中,有时需要根据特殊条件调整循环节奏,比如"找到目标值后立即终止循环""跳过当前无效值,继续下一次循环"。Kotlin提供了break和continue两个关键字,用于实现对循环的精准控制。
5.1 break 关键字(终止整个循环)
break关键字的作用是立即终止当前所在的循环,无论条件表达式是否满足,都会跳转到循环之后的代码继续执行。它适用于"找到目标后无需继续循环"的场景,比如遍历数据时找到指定元素后终止、满足某个条件时提前退出循环。
示例:遍历1到10的数字,找到第一个能被3整除的数字后终止循环:
kotlin
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的数字,只打印奇数,跳过偶数:
kotlin
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的数字则终止循环。
kotlin
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。
输出结果:
符合条件的数字:5
符合条件的数字:15
循环终止
六、实用场景举例
前面介绍了while和do/while循环的基础用法和差异,下面结合实际开发中的常见场景,展示两者的具体应用,帮助读者更好地理解"何时用哪种循环"。
6.1 while 场景(遍历未知长度数据、条件满足时循环)
6.1.1 场景1:遍历未知长度的数据流
需求:模拟读取一个未知长度的数据流(如从文件读取内容,直到读取到结束标记"EOF"),每读取一条数据就打印,数据流为空则不执行循环。由于数据流长度未知且可能为空,适合用while循环。
kotlin
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
}
输出结果:
erlang
开始读取数据流...
读取到数据:数据1
读取到数据:数据2
数据流读取完毕
6.1.2 场景2:条件满足时循环执行重试逻辑
需求:系统启动时连接数据库,若连接失败则每隔3秒重试一次,直到连接成功或达到最大重试次数(5次)。由于初始可能连接成功,无需重试,适合用while循环。
kotlin
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
}
输出结果:
erlang
数据库连接失败,1 次重试,3秒后重试...
数据库连接失败,2 次重试,3秒后重试...
数据库连接成功!
6.2 do/while 场景(表单验证、初始化后需校验的逻辑)
6.2.1 场景1:表单输入验证(至少输入一次)
需求:用户注册时输入密码,要求密码长度至少8位且包含数字和字母,若密码不满足要求则反复提示输入,直到输入符合要求的密码。由于必须让用户至少输入一次密码,适合用do/while循环。
kotlin
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循环。
kotlin
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)
}
}
输出结果:
ini
配置初始化无效,1 次初始化,准备重新初始化...
配置初始化成功,配置信息:Config(serverUrl=http://localhost, port=8080)
七、总结与使用建议
7.1 核心知识点回顾(语法、流程、差异)
- while循环核心:语法为"while(条件){循环体}",执行流程是"先判断后执行",初始条件不满足时循环体一次都不执行,适合"可能不执行"的场景。
- do/while循环核心:语法为"do{循环体}while(条件);",执行流程是"先执行后判断",无论初始条件是否满足,循环体至少执行一次,适合"必须执行一次"的场景。
- 循环控制核心:break用于终止整个循环,continue用于跳过当前迭代;使用continue时需提前更新循环变量,避免无限循环。
- 核心差异:执行顺序不同(判断时机),导致初始条件不满足时的表现不同(是否执行一次),进而决定适用场景不同。
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循环,核心判断标准只有一个:循环体是否需要至少执行一次。可按照以下步骤快速判断:
- 明确业务需求:思考"循环体是否存在'一次都不执行'的合理场景";
- 若存在"一次都不执行"的场景:选择while循环,如遍历可能为空的集合、条件初始就不满足的循环;
- 若"必须至少执行一次":选择do/while循环,如用户输入验证、初始化后校验的逻辑;
- 复杂场景结合循环控制:当需要提前终止循环或跳过无效迭代时,配合break和continue使用,精准控制循环节奏。
此外,还需注意:while和do/while循环更适合"不确定次数"的条件驱动场景,若循环次数固定或需要遍历集合/区间,优先选择for循环,让代码更简洁直观。
掌握while和do/while循环的核心差异和适用场景,结合break、continue进行精准控制,能让你在处理"条件驱动"的循环场景时游刃有余,写出更高效、更易读的Kotlin代码。