控制流程
控制流程决定代码的执行顺序。Go的控制语句很简洁,没有括号!我刚开始写Go的时候,总是不自觉地加上括号,后来才逐渐适应这种简洁的风格。
if 条件语句
基本用法
age := 18
if age >= 18 {
fmt.Println("成年人")
} else if age >= 12 {
fmt.Println("青少年")
} else {
fmt.Println("儿童")
}
和Java/JS的区别
// Java/JS - 条件需要括号
if (age >= 18) { ... }
// Go - 不需要括号!
if age >= 18 { ... }
if 的特殊写法(带初始化)
Go的 if 可以在条件前加一个初始化语句,变量只在if块内有效:
// 普通写法
err := doSomething()
if err != nil {
fmt.Println(err)
}
// err 在这里还能访问
// Go特有写法(推荐!)
if err := doSomething(); err != nil {
fmt.Println(err)
}
// err 在这里不能访问了(更安全)
这种写法在错误处理中非常常见:
if data, err := fetchData(); err != nil {
log.Fatal(err)
} else {
process(data)
}
for 循环
Go只有 for,没有 while 和 do-while!但 for 可以变身:
标准for循环
// 类似 Java: for (int i = 0; i < 10; i++)
for i := 0; i < 10; i++ {
fmt.Println(i)
}
while 风格
// 类似 while(n < 10)
n := 0
for n < 10 {
fmt.Println(n)
n++
}
无限循环
// 类似 while(true)
for {
fmt.Println("永远循环")
// 需要 break 或 return 跳出
}
for-range 遍历
超级好用的遍历语法!
// 遍历切片/数组
nums := []int{1, 2, 3, 4, 5}
for index, value := range nums {
fmt.Printf("索引: %d, 值: %d\n", index, value)
}
// 只要值,忽略索引
for _, value := range nums {
fmt.Println(value)
}
// 只要索引
for index := range nums {
fmt.Println(index)
}
// 遍历字符串
for i, char := range "Hello世界" {
fmt.Printf("%d: %c\n", i, char)
}
// 遍历Map
m := map[string]int{"a": 1, "b": 2}
for key, value := range m {
fmt.Printf("%s: %d\n", key, value)
}
break 和 continue
for i := 0; i < 10; i++ {
if i == 3 {
continue // 跳过本次,继续下一次
}
if i == 7 {
break // 跳出整个循环
}
fmt.Println(i) // 输出: 0, 1, 2, 4, 5, 6
}
带标签的break(跳出多层循环)
outer:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
break outer // 直接跳出外层循环
}
fmt.Printf("(%d, %d) ", i, j)
}
}
// 输出: (0, 0) (0, 1) (0, 2) (1, 0)
switch 语句
Go的switch超级好用,不需要break!
基本用法
day := "Monday"
switch day {
case "Monday":
fmt.Println("周一,新的开始")
case "Friday":
fmt.Println("周五,快乐的一天")
case "Saturday", "Sunday": // 多个值
fmt.Println("周末,休息日")
default:
fmt.Println("普通的一天")
}
和Java的区别
// Java需要break
switch (day) {
case "Monday":
System.out.println("周一");
break; // 必须加,否则会穿透
// ...
}
// Go自动break,不需要写!
switch day {
case "Monday":
fmt.Println("周一")
// 自动结束,不会穿透
}
想要穿透?用 fallthrough
n := 1
switch n {
case 1:
fmt.Println("一")
fallthrough // 继续执行下一个case
case 2:
fmt.Println("二")
case 3:
fmt.Println("三")
}
// 输出: 一 二
switch 无条件表达式(更灵活)
score := 85
switch {
case score >= 90:
fmt.Println("优秀")
case score >= 80:
fmt.Println("良好")
case score >= 60:
fmt.Println("及格")
default:
fmt.Println("不及格")
}
switch 带初始化
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("macOS")
case "linux":
fmt.Println("Linux")
case "windows":
fmt.Println("Windows")
default:
fmt.Printf("其他系统: %s\n", os)
}
类型switch(类型断言)
func checkType(x interface{}) {
switch v := x.(type) {
case int:
fmt.Printf("整数: %d\n", v)
case string:
fmt.Printf("字符串: %s\n", v)
case bool:
fmt.Printf("布尔: %t\n", v)
default:
fmt.Printf("未知类型: %T\n", v)
}
}
checkType(42) // 整数: 42
checkType("hello") // 字符串: hello
checkType(true) // 布尔: true
defer 延迟执行
defer 语句会在函数返回前执行,常用于资源清理:
func readFile() {
file, _ := os.Open("test.txt")
defer file.Close() // 确保文件会被关闭
// 处理文件...
// 无论如何,函数结束时都会执行 file.Close()
}
多个defer按**后进先出(LIFO)**顺序执行:
func main() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
fmt.Println("开始")
}
// 输出:
// 开始
// 3
// 2
// 1
常见用途
- 关闭文件/网络连接
- 解锁互斥锁
- 记录函数执行时间
- 恢复panic
// 记录执行时间
func slowOperation() {
start := time.Now()
defer func() {
fmt.Printf("执行耗时: %v\n", time.Since(start))
}()
time.Sleep(2 * time.Second)
}
实战案例:猜数字游戏
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
// 初始化随机数种子
rand.Seed(time.Now().UnixNano())
target := rand.Intn(100) + 1 // 1-100的随机数
attempts := 0
fmt.Println("🎮 猜数字游戏")
fmt.Println("我想了一个1-100之间的数字,你来猜!")
for {
var guess int
fmt.Print("请输入你的猜测: ")
fmt.Scan(&guess)
attempts++
switch {
case guess < target:
fmt.Println("📈 太小了,再大一点!")
case guess > target:
fmt.Println("📉 太大了,再小一点!")
default:
fmt.Printf("🎉 恭喜你,猜对了!答案就是 %d\n", target)
fmt.Printf("你一共猜了 %d 次\n", attempts)
// 评价
switch {
case attempts <= 5:
fmt.Println("评价: ⭐⭐⭐ 太厉害了!")
case attempts <= 10:
fmt.Println("评价: ⭐⭐ 不错哦!")
default:
fmt.Println("评价: ⭐ 继续加油!")
}
return
}
}
}
实战案例:九九乘法表
package main
import "fmt"
func main() {
fmt.Println("九九乘法表")
fmt.Println("================")
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d×%d=%-2d ", j, i, i*j)
}
fmt.Println()
}
}
输出:
九九乘法表
================
1×1=1
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
1×4=4 2×4=8 3×4=12 4×4=16
...
实战案例:FizzBuzz
经典面试题,展示控制流程的灵活运用:
package main
import "fmt"
func main() {
for i := 1; i <= 100; i++ {
switch {
case i%15 == 0:
fmt.Println("FizzBuzz")
case i%3 == 0:
fmt.Println("Fizz")
case i%5 == 0:
fmt.Println("Buzz")
default:
fmt.Println(i)
}
}
}
练习
- 写一个程序,打印1-100中所有的偶数
- 写一个程序,根据输入的月份判断季节
- 使用for-range遍历字符串"Go语言真简单",打印每个字符
参考答案
package main
import "fmt"
func main() {
// 1. 打印偶数
fmt.Println("=== 1-100的偶数 ===")
for i := 2; i <= 100; i += 2 {
fmt.Printf("%d ", i)
}
fmt.Println()
// 2. 判断季节
month := 5
switch month {
case 3, 4, 5:
fmt.Println("春天")
case 6, 7, 8:
fmt.Println("夏天")
case 9, 10, 11:
fmt.Println("秋天")
case 12, 1, 2:
fmt.Println("冬天")
default:
fmt.Println("无效月份")
}
// 3. 遍历字符串
str := "Go语言真简单"
fmt.Println("\n=== 遍历字符串 ===")
for i, c := range str {
fmt.Printf("位置%d: %c\n", i, c)
}
}
控制流程掌握了,下一节学习函数!
