Java转Go全过程01-基础语法部分

Java开发已经是红海一片,面临着35岁危机的压力,需要适时的调整策略,以应对可能会出现的不确定性。毕竟,命运掌握在自己手里,比掌握在公司手里 安全感会强很多。

尝试的其中一条路即为:Java转Go,也有其他的尝试,会开辟相应的专栏收录。

针对每一个部分,都会在最后准备练习题,可以多做几遍,用于巩固知识,进行多动手

后续golang语言学习过程中产生的全部内容,会发布在 web3这个专栏中。

环境准备

  1. 准备开发工具:我这里使用的是 idea家族的 GoLand
  2. GO安装程序下载安装:这里MacOs直接是安装程序 下载地址
  3. 配置本地化,类似于Maven配置aliyun的仓库地址
  4. 使用开发工具,新建一个项目
  5. 这里提供主线路,对应的每个节点,如果有不理解的,可以去问AI

打开命令行,输入go,回车,即可看到go的信息。执行下面的命令后,即可看到配置成功

ini 复制代码
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
go env

Go语言基础快速突破[基础语法](1-2周)

Go语言摒弃了语句必须以分号作为语句结束标记的习惯。

1.1 变量

注意:

  1. Go语言引入了关键字var,而类型信息放在变量名之后
  2. 出现在:=左侧的变量不应该是已经被声明过的,否则会导致编译错误
  3. 支持多重赋值功能,比如下面这个交换i和j变量的语句: i,j=j,i
  1. 变量声明方式
go 复制代码
var name string = "张三"
var age int = 25
var height float64 = 175.5
var isStudent bool = true
  1. 短变量声明(推荐方式)
go 复制代码
city := "北京"
score := 95
  1. 多变量声明
ini 复制代码
var (
    firstName = "李"
    lastName  = "四"
    phone     = "13800138000"
)

1.2 常量

在Go语言中,常量是指编译期间就已知且不可改变的值。常量可以是数值类型(包括整型、浮点型和复数类型)、布尔类型、字符串类型等。

1.2.1. 字面常量

字面常量是直接在代码中写出的固定值,没有名称。它们是表达式中的硬编码值。

类型分类:

  • 整型字面量 : 42, -5, 0600(八进制), 0xBadFace(十六进制)
  • 浮点型字面量 : 3.1415, -1.23, 2.71828
  • 复数类型字面量 : 1.2+3.4i, complex(1, 2)
  • 字符串字面量 : "Hello", 'A', raw string
  • 布尔类型字面量 : true, false
go 复制代码
package main

import "fmt"

func main() {
    fmt.Println(42)           // 整型字面量
    fmt.Println(3.1415)       // 浮点型字面量
    fmt.Println(1.2 + 3.4i)   // 复数类型字面量
    fmt.Println("Hello")      // 字符串字面量
    fmt.Println(true)         // 布尔类型字面量
}

注意:Go中没有字符类型(char)的独立类型,但可以使用单引号表示rune类型的字符字面量。

1.2.2. 常量定义

通过const关键字来定义具名常量。

基本语法:

go 复制代码
package main

import (
    "fmt"
    "math"
)

// 定义单个常量
const Pi = 3.14159

// 定义多个常量
const (
    StatusOK    = 200
    StatusError = 500
)

// 显式指定类型
const Version string = "v1.0.0"

// 使用iota(见枚举部分)
const (
    _ = iota
    KB = 1 << (10 * iota)
    MB
    GB
)

func main() {
    fmt.Println(Pi)
    fmt.Println(StatusOK, StatusError)
    fmt.Println(Version)
    fmt.Println(KB, MB, GB)
}

注意:

  • 必须在编译时确定其值。
  • 类型可选,若不指定,则为无类型常量(untyped constant),在赋值给变量时根据上下文推断类型。
  • 支持表达式计算,但所有操作数必须是常量。
1.2.3. 预定义常量 (Predeclared Constants)

Go语言内置了一些常用的预定义常量,无需额外导入即可使用。

常见预定义常量:

  • true: 布尔真值。
  • false: 布尔假值。
  • iota: 特殊常量生成器,用于创建递增序列(见下节)。
go 复制代码
package main

import "fmt"

func main() {
    fmt.Println(true, false)
}
1.2.4. 枚举 (Enumerations)

Go没有专门的enum关键字,但可以通过constiota实现枚举功能。

基本用法:

go 复制代码
package main

import "fmt"

// 使用iota定义枚举
const (
    Sunday = iota   // 0
    Monday          // 1
    Tuesday         // 2
    Wednesday       // 3
    Thursday        // 4
    Friday          // 5
    Saturday        // 6
)

// 更复杂的例子:位移操作
const (
    _  = iota       // 忽略第一个值
    KB = 1 << (10 * iota) // 1 << (10*1) = 1024
    MB                // 1 << (10*2) = 1048576
    GB                // 1 << (10*3) = 1073741824
)

// 自定义类型枚举
type Direction int

const (
    North Direction = iota
    East
    South
    West
)

func (d Direction) String() string {
    return [...]string{"North", "East", "South", "West"}[d]
}

func main() {
    fmt.Println(Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday)
    fmt.Println(KB, MB, GB)

    var dir Direction = North
    fmt.Println(dir) // 输出: North
}

注意 iota规则:

  • iota从0开始,每行递增1。
  • 同一行内所有项共享相同的iota值。
  • 可以在任意位置重新使用iota,但通常用于const块中。

高级用法:

go 复制代码
package main

import "fmt"

// 位掩码枚举
type Permission uint

const (
    Read Permission = 1 << iota // 1 << 0 = 1
    Write                      // 1 << 1 = 2
    Execute                    // 1 << 2 = 4
    Delete                     // 1 << 3 = 8
)

func (p Permission) Has(perm Permission) bool {
    return p&perm != 0
}

func main() {
    var userPerms Permission = Read | Write | Execute
    fmt.Printf("User permissions: %b\n", userPerms)
    fmt.Println("Can read?", userPerms.Has(Read))
    fmt.Println("Can delete?", userPerms.Has(Delete))
}

枚举最佳实践:

  1. 封装到类型中:为枚举定义新类型,增加类型安全性。
  2. 实现Stringer接口:便于调试和日志记录。
  3. 使用位运算:对于互斥选项,使用位掩码节省内存。
  4. 分组相关常量 :将相关的常量放在同一个const块中,提高可读性。

1.3 类型

Go语言提供了丰富的数据类型,包括基本类型、复合类型和引用类型。

1.3.1. 布尔类型 (Boolean Type)

布尔类型 表示真/假值,只有两个可能的取值:truefalse

特性:

  • 占用内存大小为1字节。
  • 默认初始值为false
  • 常用于条件判断和逻辑运算。
go 复制代码
package main

import "fmt"

func main() {
    var b1 bool      // 默认值: false
    var b2 = true   // 显式初始化
    b3 := false     // 简短声明

    fmt.Printf("b1=%t, b2=%t, b3=%t\n", b1, b2, b3)

    // 逻辑运算
    fmt.Println(b1 && b2) // false
    fmt.Println(b1 || b2) // true
    fmt.Println(!b2)      // false
}

注意:Go不允许将整型隐式转换为布尔型,必须显式比较。

1.3.2. 整型 (Integer Types)

Go支持多种整型,根据符号和长度分为以下几类:

类型 描述 范围
int 平台相关大小(32/64位) -2^31 ~ 2^31-1 或 -2^63 ~ 2^63-1
int8 有符号8位整数 -128 ~ 127
int16 有符号16位整数 -32768 ~ 32767
int32 有符号32位整数 -2147483648 ~ 2147483647
int64 有符号64位整数 -9223372036854775808 ~ 9223372036854775807
uint 无符号平台相关大小 0 ~ 2^32-1 或 0 ~ 2^64-1
uint8 无符号8位整数 0 ~ 255
uint16 无符号16位整数 0 ~ 65535
uint32 无符号32位整数 0 ~ 4294967295
uint64 无符号64位整数 0 ~ 18446744073709551615
uintptr 无符号整数,用于指针运算 足够容纳指针

示例:

go 复制代码
package main

import "fmt"

func main() {
    var a int = 10
    var b int32 = 20
    var c uint = 30

    fmt.Printf("a=%d, type=%T\n", a, a)
    fmt.Printf("b=%d, type=%T\n", b, b)
    fmt.Printf("c=%d, type=%T\n", c, c)

    // 类型转换
    d := int(b) + a
    fmt.Println("d=", d)

    // 溢出处理
    var small uint8 = 255
    small++
    fmt.Println("small after ++:", small) // 0 (溢出)
}

特殊整型:

  • rune : 别名int32,表示Unicode码点。
  • byte : 别名uint8,表示字节。
1.3.3. 浮点型 (Floating-Point Types)

Go支持两种浮点类型:

类型 描述 精度 范围
float32 单精度浮点数 约6-9位小数 ±3.4e38
float64 双精度浮点数 约15-17位小数 ±1.8e308

示例:

go 复制代码
package main

import (
    "fmt"
    "math"
)

func main() {
    var f1 float32 = 3.14159
    f2 := 2.71828 // float64

    fmt.Printf("f1=%g, type=%T\n", f1, f1)
    fmt.Printf("f2=%g, type=%T\n", f2, f2)

    // 特殊值
    fmt.Println("NaN:", math.NaN())
    fmt.Println("Positive infinity:", math.Inf(1))
    fmt.Println("Negative infinity:", math.Inf(-1))

    // 比较浮点数
    const tolerance = 1e-9
    x, y := 0.1+0.2, 0.3
    if math.Abs(x-y) < tolerance {
        fmt.Println("x ≈ y")
    }
}

注意:浮点数比较时建议使用误差范围而非直接相等比较。

1.3.4. 复数类型 (Complex Types)

Go支持复数运算,有两种类型:

类型 描述
complex64 实部和虚部均为float32
complex128 实部和虚部均为float64

构造函数:

go 复制代码
complex(real, imaginary)

示例:

go 复制代码
package main

import "fmt"

func main() {
    c1 := complex(1, 2)           // complex128
    var c2 complex64 = 3 + 4i     // complex64

    fmt.Printf("c1=%v, type=%T\n", c1, c1)
    fmt.Printf("c2=%v, type=%T\n", c2, c2)

    // 访问实部和虚部
    realPart := real(c1)
    imagPart := imag(c1)
    fmt.Printf("Real part: %g, Imaginary part: %g\n", realPart, imagPart)

    // 复数运算
    c3 := c1 + c2
    c4 := c1 * c2
    fmt.Printf("c1+c2=%v, c1*c2=%v\n", c3, c4)
}
1.3.5. 字符串 (String Type)

字符串是字节的只读序列,通常用于存储文本数据。

特性:

  • 不可变性:字符串创建后不能被修改。
  • UTF-8编码:默认采用UTF-8编码。
  • 支持两种字面量:
    • 双引号"...":可包含转义字符。
    • 反引号...:原始字符串,不支持转义。

示例:

go 复制代码
package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    s1 := "Hello, 世界"         // 双引号
    s2 := `C:\Users\John`      // 反引号

    fmt.Printf("s1=%s, len=%d\n", s1, len(s1))
    fmt.Printf("s2=%s, len=%d\n", s2, len(s2))

    // 遍历字符(rune)
    for i, r := range s1 {
        fmt.Printf("%d: %c (U+%04X)\n", i, r, r)
    }

    // 统计Unicode字符数
    fmt.Println("Rune count:", utf8.RuneCountInString(s1))

    // 字符串拼接
    s3 := s1 + " " + s2
    fmt.Println("Concatenated:", s3)

    // 子串
    sub := s1[7:9]
    fmt.Println("Substring:", sub) // "世界"

    // 字符串比较
    if s1 == "Hello, 世界" {
        fmt.Println("Strings are equal")
    }
}

注意:len()返回的是字节数而非字符数,遍历需使用range

1.3.6. 字符类型 (Character Type)

Go中没有专门的字符类型,使用以下两种方式表示字符:

  1. byte(uint8别名)
  • 用于ASCII字符。
  • 示例:var ch byte = 'A'
  1. rune(int32别名)
  • 用于Unicode码点。
  • 示例:var r rune = '世'

示例:

go 复制代码
package main

import "fmt"

func main() {
    var ascii byte = 'A'           // ASCII字符
    var unicode rune = '世'        // Unicode字符

    fmt.Printf("ascii=%c, type=%T, value=%d\n", ascii, ascii, ascii)
    fmt.Printf("unicode=%c, type=%T, value=%U\n", unicode, unicode, unicode)

    // 字符运算
    next := ascii + 1
    fmt.Printf("next character: %c\n", next) // B
}
1.3.7. 数组 (Array Type)

数组是具有固定长度的序列,每个元素类型相同。

特性:

  • 长度是类型的一部分,[3]int[5]int是不同的类型。
  • 值语义:赋值或传递时复制整个数组。
  • 索引从0开始。

声明方式:

go 复制代码
[length]Type
[...]Type{value1, value2, ...} // 自动推断长度

示例:

go 复制代码
package main

import "fmt"

func main() {
    // 声明并初始化
    var arr1 [3]int           // [0 0 0]
    arr2 := [3]int{1, 2, 3}   // [1 2 3]
    arr3 := [...]int{4, 5, 6} // 自动推断长度为3

    fmt.Printf("arr1=%v, len=%d, cap=%d\n", arr1, len(arr1), cap(arr1))
    fmt.Printf("arr2=%v, len=%d, cap=%d\n", arr2, len(arr2), cap(arr2))
    fmt.Printf("arr3=%v, len=%d, cap=%d\n", arr3, len(arr3), cap(arr3))

    // 访问和修改元素
    arr1[0] = 10
    fmt.Println("arr1 after modification:", arr1)

    // 多维数组
    var matrix [2][3]int
    matrix[0] = [3]int{1, 2, 3}
    matrix[1] = [3]int{4, 5, 6}
    fmt.Println("Matrix:", matrix)

    // 数组比较
    if arr2 == arr3 {
        fmt.Println("arr2 equals arr3")
    }

    // 作为参数传递
    printArray(arr2)
}

func printArray(a [3]int) {
    fmt.Println("Inside function:", a)
}
1.3.8. 数组切片 (Slice Type)

切片是动态大小的、灵活的视图,基于底层数组。

特性:

  • 引用语义:赋值或传递时共享底层数组。
  • 动态增长:可通过append()函数扩展。
  • 包含三个字段:指向底层数组的指针、长度、容量。

创建方式:

go 复制代码
make([]Type, len, cap)        // 使用make创建
[]Type{value1, value2, ...}   // 直接初始化
array[start:end]              // 从数组或切片截取
slice[start:end]              // 从切片截取

示例:

go 复制代码
package main

import "fmt"

func main() {
    // 创建切片
    s1 := make([]int, 3, 5)       // len=3, cap=5
    s2 := []int{1, 2, 3}          // len=3, cap=3
    s3 := s2[1:3]                 // 从s2截取,len=2, cap=2

    fmt.Printf("s1=%v, len=%d, cap=%d\n", s1, len(s1), cap(s1))
    fmt.Printf("s2=%v, len=%d, cap=%d\n", s2, len(s2), cap(s2))
    fmt.Printf("s3=%v, len=%d, cap=%d\n", s3, len(s3), cap(s3))

    // 修改切片
    s1[0] = 10
    s2[1] = 20
    s3[0] = 30
    fmt.Println("After modifications:")
    fmt.Println("s1:", s1) // [10 0 0]
    fmt.Println("s2:", s2) // [1 30 3]
    fmt.Println("s3:", s3) // [30 3]

    // 扩展切片
    s1 = append(s1, 40, 50)
    fmt.Printf("s1 after append=%v, len=%d, cap=%d\n", s1, len(s1), cap(s1))

    // 复制切片
    s4 := make([]int, len(s1))
    copy(s4, s1)
    fmt.Println("Copied slice:", s4)

    // 删除元素
    s5 := append(s1[:2], s1[3:]...)
    fmt.Println("After deletion:", s5)

    // 遍历切片
    for i, v := range s1 {
        fmt.Printf("Index %d: %d\n", i, v)
    }
}

常见操作:

  • 追加元素 : append(slice, elem1, elem2, ...)
  • 复制切片 : copy(dst, src)
  • 删除元素 : append(s[:index], s[index+1:]...)
  • 插入元素 : append(s[:index], append([]Type{newElem}, s[index:]...)...)
1.3.9. Map类型 (Map Type)

Map是无序的键值对集合,也称为哈希表或字典。

特性:

  • 引用语义:赋值或传递时共享底层数据结构。
  • 键必须是可比较的类型(如整数、字符串、指针等)。
  • 值可以是任意类型。

创建方式:

go 复制代码
make(map[KeyType]ValueType, initialCapacity)  // 使用make创建
map[KeyType]ValueType{key1: value1, ...}      // 直接初始化

示例:

go 复制代码
package main

import "fmt"

func main() {
    // 创建map
    m1 := make(map[string]int)
    m2 := map[string]string{
        "name": "Alice",
        "city": "Beijing",
    }

    fmt.Printf("m1=%v, len=%d\n", m1, len(m1))
    fmt.Printf("m2=%v, len=%d\n", m2, len(m2))

    // 添加/更新元素
    m1["age"] = 25
    m1["score"] = 95
    m2["age"] = "25"

    // 访问元素
    if age, ok := m1["age"]; ok {
        fmt.Println("Age:", age)
    }

    // 删除元素
    delete(m1, "score")

    // 遍历map
    fmt.Println("m1 contents:")
    for k, v := range m1 {
        fmt.Printf("  %s: %d\n", k, v)
    }

    fmt.Println("m2 contents:")
    for k, v := range m2 {
        fmt.Printf("  %s: %s\n", k, v)
    }

    // 检查键是否存在
    if _, exists := m1["score"]; !exists {
        fmt.Println("Score not found")
    }

    // 作为参数传递
    printMap(m1)
}

func printMap(m map[string]int) {
    fmt.Println("Inside function:", m)
}

注意事项:

  1. 并发不安全 : 多个goroutine同时读写map会导致panic,需使用sync.RWMutex保护。
  2. 无序性: map遍历顺序不确定,每次可能不同。
  3. 零值 : map的零值是nil,不能直接赋值,需先初始化。
附录:1~3节 习题
go 复制代码
package main

import (
    "fmt"
    "math"
    "strconv"
    "strings"
)

// 练习1: 字面常量与类型推断
func practiceLiterals() {
    fmt.Println("=== 练习1: 字面常量与类型 ===")

    // 练习1.1: 默认类型推断
    // 请观察以下字面常量的默认类型:
    // - 整数: 42
    // - 浮点数: 3.1415
    // - 字符串: "Hello"
    // - 字符: 'A'
    // - 复数: 1+2i
    // TODO: 使用fmt.Printf("%T")打印每个字面量的类型

    // 练习1.2: 显式类型指定
    // 请显式声明以下类型:
    // - int32: 100
    // - float32: 2.5
    // - complex64: 1+3i
    // TODO: 添加显式类型声明代码

    // 练习1.3: 类型转换
    // 请进行以下转换:
    // - float64(3.14) -> int
    // - "123" -> int
    // - int8(127) -> int16
    // TODO: 添加类型转换代码
}

// 练习2: 常量定义与枚举
func practiceConstants() {
    fmt.Println("\n=== 练习2: 常量定义 ===")

    // 练习2.1: 单常量声明
    // 请声明以下常量:
    // - PI = 3.1415926
    // - LANGUAGE = "Go"
    // - MAX_USERS = 1000
    // TODO: 添加单常量声明代码

    // 练习2.2: 常量块
    // 请使用const块声明:
    // - 状态码: OK=200, NotFound=404, Error=500
    // TODO: 添加常量块声明代码

    // 练习2.3: iota枚举
    // 请使用iota定义:
    // - 季节: Spring=1, Summer, Autumn, Winter
    // - 权限: Read=1<<iota, Write, Execute
    // TODO: 添加iota枚举代码

    // 练习2.4: 无类型常量
    // 请声明无类型常量并观察类型推断:
    // - const Untyped = 100
    // - var a int = Untyped
    // - var b float64 = Untyped
    // TODO: 添加无类型常量代码
}

// 练习3: 基础数据类型
func practiceBasicTypes() {
    fmt.Println("\n=== 练习3: 基础类型 ===")

    // 练习3.1: 整型溢出
    // 请观察以下溢出行为:
    // - uint8(255) + 1
    // - int8(127) + 1
    // TODO: 添加溢出观察代码

    // 练习3.2: 浮点数比较
    // 请实现浮点数比较函数:
    // - 当差值小于1e-9时返回true
    // TODO: 实现floatEquals函数

    // 练习3.3: 复数运算
    // 请计算:
    // - (1+2i) + (3+4i)
    // - (2+3i) * (1-1i)
    // TODO: 添加复数运算代码

    // 练习3.4: 字符串操作
    // 请操作字符串"Hello, 世界":
    // - 获取字节长度
    // - 获取Unicode字符数
    // - 遍历并打印每个字符的Unicode码点
    // TODO: 添加字符串操作代码

    // 练习3.5: rune与byte
    // 请声明:
    // - byte: 'A'
    // - rune: '世'
    // TODO: 添加rune/byte声明代码
}

// 练习4: 复合类型
func practiceCompositeTypes() {
    fmt.Println("\n=== 练习4: 复合类型 ===")

    // 练习4.1: 数组
    // 请声明并初始化:
    // - 长度为3的整型数组: [1,2,3]
    // - 长度为5的字符串数组(自动推断)
    // TODO: 添加数组声明代码

    // 练习4.2: 切片
    // 请操作切片:
    // - 从数组[1,2,3,4,5]创建切片[2,3,4]
    // - 修改切片后观察原数组变化
    // - 使用append扩展切片
    // TODO: 添加切片操作代码

    // 练习4.3: Map
    // 请操作Map:
    // - 创建map[string]int: {"apple":10, "banana":20}
    // - 实现getOrDefault函数
    // - 遍历map并打印键值对
    // TODO: 添加Map操作代码

    // 练习4.4: 类型组合
    // 请设计结构体:
    // - Student: Name(string), Age(int), Grades(map[string]float64)
    // - 初始化实例并添加成绩"Math":95.5
    // TODO: 添加类型组合代码
}

// 练习5: 综合应用
func practiceAdvanced() {
    fmt.Println("\n=== 练习5: 综合应用 ===")

    // 练习5.1: 位掩码权限检查
    // 请实现:
    // - 用户权限: Read|Write
    // - 检查用户是否有Read权限
    // TODO: 添加位掩码代码

    // 练习5.2: 浮点数精度处理
    // 请实现:
    // - 计算0.1+0.2+0.3,并与0.6比较
    // TODO: 添加浮点数精度处理代码

    // 练习5.3: 字符串解析
    // 请实现:
    // - 解析"123,456,789"为整型切片
    // TODO: 添加字符串解析代码

    // 练习5.4: 类型转换链
    // 请实现:
    // - string("255") -> int -> uint8 -> int16
    // TODO: 添加类型转换链代码
}

// ======== 答案参考实现 (练习时请勿展开) ========

// 练习3.2答案
func floatEquals(a, b float64) bool {
    return math.Abs(a-b) < 1e-9
}

// 练习4.3答案
func getOrDefault(m map[string]int, key string, def int) int {
    if v, ok := m[key]; ok {
       return v
    }
    return def
}

// 练习4.4答案
type Student struct {
    Name   string
    Age    int
    Grades map[string]float64
}

// 练习5.3答案
func parseInts(s string) []int {
    var result []int
    for _, part := range strings.Split(s, ",") {
       if num, err := strconv.Atoi(part); err == nil {
          result = append(result, num)
       }
    }
    return result
}

func main() {
    practiceLiterals()
    practiceConstants()
    practiceBasicTypes()
    practiceCompositeTypes()
    practiceAdvanced()

    fmt.Println("\n=== 练习完成 ===")
    fmt.Println("请完成所有TODO部分的代码,运行验证结果!")
}

1.4 流程控制

Go语言的流程控制结构简洁而强大,包括条件语句、选择语句、循环语句和跳转语句。以下是对这些结构的全面解析:

1.4.1. 条件语句 (If Statements)

基本语法

go 复制代码
if condition {
    // 条件为真时执行的代码
} else if condition2 {
    // 第二个条件为真时执行的代码
} else {
    // 所有条件都为假时执行的代码
}

示例

简单条件判断

go 复制代码
package main

import "fmt"

func main() {
    x := 10

    if x > 5 {
        fmt.Println("x大于5")
    } else {
        fmt.Println("x小于等于5")
    }
}

带初始化的if语句

go 复制代码
if val := getValue(); val > 100 {
    fmt.Println("值大于100:", val)
} else if val > 50 {
    fmt.Println("值在51-100之间:", val)
} else {
    fmt.Println("值小于等于50:", val)
}

条件表达式类型

  • 必须是布尔类型(bool
  • 不允许隐式类型转换(如不能用整型代替布尔型)
go 复制代码
// 错误示例
if 1 {  // 编译错误:非布尔类型
    fmt.Println("这不会编译")
}

// 正确做法
if 1 == 1 {
    fmt.Println("正确")
}

空接口类型断言

go 复制代码
var i interface{} = "hello"

if s, ok := i.(string); ok {
    fmt.Printf("i是字符串: %s\n", s)
} else {
    fmt.Println("i不是字符串")
}
1.4.2. 选择语句 (Switch Statements)

基本语法

go 复制代码
switch expression {
case value1:
    // 代码块
case value2, value3:  // 多值匹配
    // 代码块
default:
    // 默认代码块
}

示例

表达式switch

go 复制代码
package main

import "fmt"

func main() {
    day := "Wednesday"

    switch day {
    case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
        fmt.Println("工作日")
    case "Saturday", "Sunday":
        fmt.Println("周末")
    default:
        fmt.Println("无效日期")
    }
}

类型switch

go 复制代码
func printType(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)
    }
}

func main() {
    printType(42)
    printType("hello")
    printType(true)
}

无表达式switch

相当于多个if-else语句的简洁形式:

go 复制代码
func grade(score int) string {
    switch {
    case score >= 90:
        return "A"
    case score >= 80:
        return "B"
    case score >= 70:
        return "C"
    case score >= 60:
        return "D"
    default:
        return "F"
    }
}

fallthrough行为

  • 默认不穿透:每个case执行完后自动跳出
  • 显式穿透 :使用fallthrough关键字继续执行下一个case
go 复制代码
switch num := 2; num {
case 1:
    fmt.Println("1")
    fallthrough
case 2:
    fmt.Println("2")
    fallthrough
case 3:
    fmt.Println("3")
default:
    fmt.Println("default")
}
// 输出:
// 2
// 3
// default

注意:fallthrough必须位于case块的最后一行[^1]。

1.4.3. 循环语句 (For Statements)

Go只有for循环,但支持三种形式:

经典for循环

go 复制代码
for initialization; condition; post {
    // 循环体
}

示例

go 复制代码
for i := 0; i < 5; i++ {
    fmt.Printf("i = %d\n", i)
}

条件循环(while循环的替代品)

go 复制代码
for condition {
    // 循环体
}

示例

go 复制代码
sum := 1
for sum < 1000 {
    sum += sum
}
fmt.Println("Sum:", sum)

无限循环

go 复制代码
for {
    // 无限循环
    if someCondition {
        break  // 需要手动退出
    }
}

示例

go 复制代码
count := 0
for {
    count++
    if count >= 5 {
        break
    }
    fmt.Println("Count:", count)
}

范围循环 (Range Clause)

用于遍历数组、切片、字符串、map和channel。

基本语法

go 复制代码
for index, value := range collection {
    // 使用index和value
}

示例

数组/切片

go 复制代码
numbers := []int{10, 20, 30}
for i, num := range numbers {
    fmt.Printf("索引 %d: 值 %d\n", i, num)
}

字符串(遍历rune):

go 复制代码
text := "Hello, 世界"
for i, r := range text {
    fmt.Printf("%d: %c (U+%04X)\n", i, r, r)
}

Map

go 复制代码
m := map[string]int{"a": 1, "b": 2}
for k, v := range m {
    fmt.Printf("%s: %d\n", k, v)
}

忽略索引/值

go 复制代码
// 只关心值
for _, value := range collection {
    // ...
}

// 只关心索引
for index := range collection {
    // ...
}

循环控制

  • break:跳出当前循环
  • continue:跳过本次迭代,继续下一次
  • goto:跳转到标签(见跳转语句)
go 复制代码
for i := 0; i < 10; i++ {
    if i == 3 {
        continue  // 跳过i=3
    }
    if i == 7 {
        break     // 当i=7时退出循环
    }
    fmt.Println(i)
}
// 输出: 0,1,2,4,5,6
1.4.4. 跳转语句 (Jump Statements)

break

  • 基本break:跳出当前循环或switch
  • 带标签的break:跳出指定嵌套循环
go 复制代码
outerLoop:
for i := 0; i < 3; i++ {
    for j := 0; j < 3; j++ {
        if i == 1 && j == 1 {
            break outerLoop  // 直接跳出外层循环
        }
        fmt.Printf("%d-%d\n", i, j)
    }
}
// 输出: 0-0, 0-1, 0-2, 1-0

continue

  • 基本continue:跳过当前循环迭代
  • 带标签的continue:跳过指定嵌套循环的当前迭代
go 复制代码
outerLoop:
for i := 0; i < 3; i++ {
    for j := 0; j < 3; j++ {
        if j == 1 {
            continue outerLoop  // 跳过外层循环的剩余部分
        }
        fmt.Printf("%d-%d\n", i, j)
    }
}
// 输出: 0-0, 1-0, 2-0

goto

无条件跳转到标签位置。

语法

go 复制代码
goto label
// ...
label:
// 跳转目标

示例

go 复制代码
func main() {
    i := 0
loop:
    if i < 5 {
        fmt.Println(i)
        i++
        goto loop
    }
}

实际应用

go 复制代码
func processFile(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer file.Close()

    data := make([]byte, 1024)
    for {
        n, err := file.Read(data)
        if err == io.EOF {
            goto done
        }
        if err != nil {
            goto error
        }
        processData(data[:n])
    }

done:
    fmt.Println("处理完成")
    return nil

error:
    fmt.Println("处理出错")
    return err
}

fallthrough

已在switch语句中介绍,仅用于switch语句中。

return

函数返回,不属于循环控制但常与流程控制结合使用。

go 复制代码
func findValue(slice []int, target int) int {
    for i, v := range slice {
        if v == target {
            return i  // 找到后立即返回
        }
    }
    return -1  // 未找到
}

注意:

  1. if语句
  • 优先使用带初始化的if语句减少变量作用域
  • 避免过长的if-else链,考虑使用switch替代
  1. switch语句
  • 优先使用无表达式switch处理多条件分支
  • 使用类型switch处理interface{}类型判断
  • 避免不必要的fallthrough
  1. for循环
  • 优先使用range遍历集合
  • 无限循环务必包含退出条件
  • 避免在循环内修改正在遍历的集合
  1. 跳转语句
  • break/continue:合理使用标签处理嵌套循环
  • goto:谨慎使用,优先用函数和循环控制替代
  • return:早期返回减少嵌套层级
  1. 性能考虑
  • 对于大切片,range比传统for循环略慢(因边界检查)
  • 在性能敏感场景,可考虑传统for循环
go 复制代码
// 传统for循环(可能更快)
for i := 0; i < len(slice); i++ {
    // ...
}

// range循环(更简洁)
for _, v := range slice {
    // ...
}
附录:流程控制部分习题
go 复制代码
package main

import (
    "bufio"
    "fmt"
    "os"
    "strconv"
    "unicode"
)

// ============ 练习1: 条件语句 ============
func conditionals() {
    fmt.Println("=== 条件语句 ===")

    // 1. 判断偶数
    num := 10
    if num%2 == 0 {
       fmt.Println(num, "是偶数")
    } else {
       fmt.Println(num, "是奇数")
    }

    // 2. 带初始化的if
    if length := len("Hello World"); length > 5 {
       fmt.Println("字符串长度超过5:", length)
    }

    // 3. 分数等级
    fmt.Printf("分数85的等级: %s\n", getGrade(85))
    fmt.Printf("分数50的等级: %s\n", getGrade(50))

    // 4. 空接口断言
    var i interface{} = "hello"
    if s, ok := i.(string); ok {
       fmt.Printf("i是字符串: %s\n", s)
    } else if n, ok := i.(int); ok {
       fmt.Printf("i是整数: %d\n", n)
    } else {
       fmt.Printf("i是其他类型: %T\n", i)
    }
}

func getGrade(score int) string {
    if score >= 90 {
       return "A"
    } else if score >= 80 {
       return "B"
    } else if score >= 70 {
       return "C"
    } else if score >= 60 {
       return "D"
    }
    return "F"
}

// ============ 练习2: 选择语句 ============
func switches() {
    fmt.Println("\n=== 选择语句 ===")

    // 1. 季度判断
    month := 5
    switch {
    case month >= 1 && month <= 3:
       fmt.Println("Q1")
    case month <= 6:
       fmt.Println("Q2")
    case month <= 9:
       fmt.Println("Q3")
    default:
       fmt.Println("Q4")
    }

    // 2. 类型switch
    var val interface{} = true
    switch v := val.(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)
    }

    // 3. 无表达式switch
    fmt.Printf("分数75的等级: %s\n", getGradeSwitch(75))

    // 4. fallthrough
    fallthroughDemo(1)
}

func getGradeSwitch(score int) string {
    switch {
    case score >= 90:
       return "A"
    case score >= 80:
       return "B"
    case score >= 70:
       return "C"
    case score >= 60:
       return "D"
    default:
       return "F"
    }
}

func fallthroughDemo(n int) {
    switch n {
    case 1:
       fmt.Print("1,")
       fallthrough
    case 2:
       fmt.Print("2,")
       fallthrough
    case 3:
       fmt.Println("3")
    }
}

// ============ 练习3: 循环语句 ============
func loops() {
    fmt.Println("\n=== 循环语句 ===")

    // 1. 经典for循环
    for i := 1; i <= 5; i++ {
       fmt.Println(i)
    }

    // 2. 条件循环(阶乘)
    fmt.Printf("5的阶乘: %d\n", factorial(5))

    // 3. range遍历map
    m := map[string]int{"a": 1, "b": 2, "c": 3}
    for k, v := range m {
       fmt.Printf("%s: %d\n", k, v)
    }

    // 4. 无限循环
    fmt.Println("输入exit退出:")
    scanner := bufio.NewScanner(os.Stdin)
    for {
       scanner.Scan()
       if scanner.Text() == "exit" {
          break
       }
    }
}

func factorial(n int) int {
    result := 1
    for i := 1; i <= n; i++ {
       result *= i
    }
    return result
}

// ============ 练习4: 跳转语句 ============
func jumps() {
    fmt.Println("\n=== 跳转语句 ===")

    // 1. break退出
    nums := []int{1, 2, 3, 4, 5}
    for _, num := range nums {
       if num == 3 {
          fmt.Println("找到3,退出")
          break
       }
       fmt.Println(num)
    }

    // 2. continue跳过偶数
    for _, num := range nums {
       if num%2 == 0 {
          continue
       }
       fmt.Println(num)
    }

    // 3. 带标签的break
outer:
    for i := 0; i < 3; i++ {
       for j := 0; j < 3; j++ {
          if i+j == 2 {
             fmt.Printf("跳出外层循环: i=%d, j=%d\n", i, j)
             break outer
          }
          fmt.Printf("%d-%d\n", i, j)
       }
    }

    // 4. goto计数器
    i := 0
loop:
    if i < 5 {
       fmt.Println(i)
       i++
       goto loop
    }
}

// ============ 练习5: 综合应用 ============
func advanced() {
    fmt.Println("\n=== 综合应用 ===")

    // 1. 猜数字游戏
    fmt.Println("猜数字游戏 (1-100):")
    target := 42 // 假设随机生成42
    scanner := bufio.NewScanner(os.Stdin)
    for {
       scanner.Scan()
       input, _ := strconv.Atoi(scanner.Text())
       if input == target {
          fmt.Println("猜对了!")
          break
       } else if input < target {
          fmt.Println("太小")
       } else {
          fmt.Println("太大")
       }
    }

    // 2. 字符串分析
    analyzeString("Hello, 世界!")
}

func analyzeString(s string) {
    var letters, digits, spaces, others int
    for _, r := range s {
       switch {
       case unicode.IsLetter(r):
          letters++
       case unicode.IsDigit(r):
          digits++
       case unicode.IsSpace(r):
          spaces++
       default:
          others++
       }
    }
    fmt.Printf("字母:%d, 数字:%d, 空格:%d, 其他:%d\n", letters, digits, spaces, others)
}

func calculate(operation string, num int) int {
    switch operation {
    case "add":
       return num + 10
    case "subtract":
       return num - 10
    case "multiply":
       return num * 10
    case "divide":
       if num != 0 {
          return num / 10
       }
       return 0
    default:
       return num
    }
}

// ============ 主函数 ============
func main() {
    fmt.Println("Go语言流程控制练习题")
    fmt.Println("====================")

    // 练习1: 条件语句
    conditionals()

    // 练习2: 选择语句
    switches()

    // 练习3: 循环语句
    loops()

    // 练习4: 跳转语句
    jumps()

    // 练习5: 综合应用
    advanced()

    fmt.Println("\n====================")
    fmt.Println("练习完成!检查所有TODO部分并运行验证。")
}

1.5 函数

1.5.1. 函数定义 (Function Definition)

基本语法

go 复制代码
func functionName(param1 type1, param2 type2) returnType {
    // 函数体
    return value
}

关键特性

1.1 基本函数定义
go 复制代码
package main

import "fmt"

// 无参数无返回值
func greet() {
    fmt.Println("Hello, Go!")
}

// 有参数无返回值
func greetPerson(name string) {
    fmt.Printf("Hello, %s!\n", name)
}

// 有参数有返回值
func add(a, b int) int {  // 相同类型可简写
    return a + b
}

// 多参数不同时类型
func createPoint(x, y int, name string) {
    fmt.Printf("Point %s: (%d, %d)\n", name, x, y)
}
1.2 命名规则
  • 遵循Go的命名规范:
    • 首字母大写:公开函数(包外可见)
    • 首字母小写:私有函数(包内可见)
  • 推荐使用驼峰命名法:CalculateSum
1.3 函数签名

函数的类型由其参数和返回值决定,称为函数签名:

go 复制代码
func(int, int) int           // add函数的签名
func(string)                 // greetPerson函数的签名
func()                       // greet函数的签名
1.5.2. 函数调用 (Function Call)

基本调用方式

go 复制代码
package main

func main() {
    // 无参函数调用
    greet()

    // 带参函数调用
    greetPerson("Alice")

    // 带返回值函数调用
    sum := add(3, 5)
    fmt.Println("Sum:", sum)

    // 多参数函数调用
    createPoint(10, 20, "Origin")
}

特殊调用方式

2.1 忽略返回值
go 复制代码
// 只关心副作用,不关心返回值
fmt.Println("Hello")  // 忽略返回值(int, error)

// 显式忽略返回值
_, err := os.Open("file.txt")
if err != nil {
    // 处理错误
}
2.2 链式调用
go 复制代码
// 当函数返回值可用于下一个函数时
result := strings.ToUpper(strings.TrimSpace("  hello  "))
fmt.Println(result) // "HELLO"
2.3 方法调用

函数作为类型的一部分:

go 复制代码
type Calculator struct{}

func (c Calculator) Multiply(a, b int) int {
    return a * b
}

func main() {
    calc := Calculator{}
    product := calc.Multiply(3, 4)
    fmt.Println("Product:", product)
}
1.5.3. 不定参数 (Variadic Parameters)

基本语法

go 复制代码
func functionName(param1 type1, params ...type2) returnType {
    // 函数体
}

关键特性

3.1 基本使用
go 复制代码
package main

import "fmt"

// 不定参数函数
func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

// 混合参数
func printInfo(prefix string, values ...int) {
    fmt.Print(prefix, ": ")
    for _, v := range values {
        fmt.Print(v, " ")
    }
    fmt.Println()
}

func main() {
    // 调用不定参数函数
    fmt.Println("Sum of 1,2,3:", sum(1, 2, 3))
    fmt.Println("Sum of 4,5,6,7:", sum(4, 5, 6, 7))

    // 混合参数调用
    printInfo("Numbers", 10, 20, 30)

    // 传递切片作为不定参数
    nums := []int{1, 2, 3, 4}
    fmt.Println("Sum of slice:", sum(nums...))  // 注意...操作符
}
3.2 注意事项
  • 不定参数必须是函数的最后一个参数
  • 不定参数在函数内部被视为切片
  • 可以传递切片作为不定参数(使用...操作符)
3.3 性能考虑
  • 不定参数会创建新的切片,可能影响性能
  • 对于性能敏感的代码,考虑使用切片参数
go 复制代码
// 性能优化版本
func sumOptimized(numbers []int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}
1.5.4. 多返回值 (Multiple Return Values)

基本语法

go 复制代码
func functionName(param1 type1) (returnType1, returnType2) {
    // 函数体
    return value1, value2
}

关键特性

4.1 基本使用
go 复制代码
package main

import "fmt"

// 多返回值函数
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("除零错误")
    }
    return a / b, nil
}

// 命名返回值
func parseValue(s string) (value int, err error) {
    value, err = strconv.Atoi(s)
    return  // 自动返回命名的返回值
}

// 多个相同类型返回值
func minMax(numbers []int) (min, max int) {
    if len(numbers) == 0 {
        return
    }
    min, max = numbers[0], numbers[0]
    for _, num := range numbers {
        if num < min {
            min = num
        }
        if num > max {
            max = num
        }
    }
    return
}

func main() {
    // 接收多返回值
    result, err := divide(10, 3)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result)
    }

    // 忽略部分返回值
    value, _ := parseValue("123")
    fmt.Println("Value:", value)

    // 命名返回值
    min, max := minMax([]int{3, 1, 4, 1, 5})
    fmt.Printf("Min: %d, Max: %d\n", min, max)
}
4.2 命名返回值
  • 命名返回值会被初始化为对应类型的零值
  • 可以使用简单的return语句返回
  • 命名返回值在文档中有助于理解函数
4.3 错误处理

Go推荐使用多返回值进行错误处理:

go 复制代码
// 标准库中的例子
file, err := os.Open("filename.txt")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

// 自定义错误处理
func safeDivide(a, b int) (int, bool) {
    if b == 0 {
        return 0, false
    }
    return a / b, true
}
1.5.5. 匿名函数与闭包 (Anonymous Functions and Closures)

基本语法

go 复制代码
// 匿名函数定义
func(parameters) returnType {
    // 函数体
}

// 闭包:捕获外部变量的匿名函数
func() {
    // 使用外部变量
}

关键特性

5.1 匿名函数
go 复制代码
package main

import "fmt"

func main() {
    // 定义并立即调用匿名函数
    func() {
        fmt.Println("立即调用的匿名函数")
    }()

    // 将匿名函数赋值给变量
    greet := func(name string) {
        fmt.Printf("Hello, %s!\n", name)
    }
    greet("Alice")

    // 匿名函数作为参数
    applyOperation(5, 3, func(a, b int) int {
        return a * b
    })

    // 匿名函数作为返回值
    adder := createAdder(10)
    fmt.Println(adder(5))  // 15
}

// 接受函数作为参数
func applyOperation(a, b int, op func(int, int) int) int {
    return op(a, b)
}

// 返回函数
func createAdder(offset int) func(int) int {
    return func(x int) int {
        return x + offset
    }
}
5.2 闭包 (Closure)

闭包是能够访问外部作用域变量的函数:

go 复制代码
package main

import "fmt"

func main() {
    // 基本闭包示例
    x := 10
    increment := func() int {
        x++  // 捕获并修改外部变量x
        return x
    }
    fmt.Println(increment())  // 11
    fmt.Println(increment())  // 12
    fmt.Println("x =", x)     // 12

    // 计数器生成器
    counter := createCounter()
    fmt.Println(counter())  // 1
    fmt.Println(counter())  // 2
    fmt.Println(counter())  // 3

    // 私有状态
    bank := createBankAccount(100)
    fmt.Println(bank.balance)  // 100
    bank.deposit(50)
    fmt.Println(bank.balance)  // 150
    fmt.Println(bank.withdraw(30))  // true
    fmt.Println(bank.balance)  // 120
}

// 计数器生成器
func createCounter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

// 银行账户(封装私有状态)
func createBankAccount(initialBalance int) *BankAccount {
    balance := initialBalance
    return &BankAccount{
        balance: balance,
        deposit: func(amount int) {
            balance += amount
        },
        withdraw: func(amount int) bool {
            if balance >= amount {
                balance -= amount
                return true
            }
            return false
        },
    }
}

type BankAccount struct {
    balance  int
    deposit  func(int)
    withdraw func(int) bool
}
5.3 闭包注意事项

5.3.1 循环中的闭包陷阱

go 复制代码
// 错误示例:所有闭包共享同一个变量
func badExample() {
    var funcs []func()
    for i := 0; i < 3; i++ {
        funcs = append(funcs, func() {
            fmt.Println(i)  // 所有闭包都打印3
        })
    }
    for _, f := range funcs {
        f()
    }
}

// 正确做法1:使用参数
func goodExample1() {
    var funcs []func()
    for i := 0; i < 3; i++ {
        funcs = append(funcs, func(n int) func() {
            return func() {
                fmt.Println(n)
            }
        }(i))
    }
    for _, f := range funcs {
        f()
    }
}

// 正确做法2:创建局部变量
func goodExample2() {
    var funcs []func()
    for i := 0; i < 3; i++ {
        i := i  // 创建局部变量副本
        funcs = append(funcs, func() {
            fmt.Println(i)
        })
    }
    for _, f := range funcs {
        f()
    }
}

5.3.2 性能考虑

  • 闭包会延长捕获变量的生命周期
  • 过度使用闭包可能导致内存泄漏
  • 在性能敏感场景谨慎使用
5.4 闭包的典型应用

5.4.1 装饰器模式

go 复制代码
func logDuration(f func()) func() {
    return func() {
        start := time.Now()
        f()
        fmt.Printf("耗时: %v\n", time.Since(start))
    }
}

func main() {
    slowFunc := func() {
        time.Sleep(1 * time.Second)
    }
    loggedFunc := logDuration(slowFunc)
    loggedFunc()
}

5.4.2 函数工厂

go 复制代码
func createMultiplier(factor int) func(int) int {
    return func(x int) int {
        return x * factor
    }
}

func main() {
    double := createMultiplier(2)
    triple := createMultiplier(3)
    fmt.Println(double(5))  // 10
    fmt.Println(triple(5))  // 15
}

5.4.3 延迟初始化

go 复制代码
func lazyInit() func() *MyStruct {
    var instance *MyStruct
    var once sync.Once
    return func() *MyStruct {
        once.Do(func() {
            instance = &MyStruct{/* 初始化 */}
        })
        return instance
    }
}

注意

  1. 函数设计原则
  • 单一职责:一个函数只做一件事
  • 保持简短:理想函数长度不超过20行
  • 参数控制:通常不超过3-4个参数
  • 返回值:优先使用命名返回值
  1. 错误处理
  • 优先使用多返回值处理错误
  • 避免在函数中直接panic
  • 提供清晰的错误信息
go 复制代码
// 好:明确错误处理
func readFile(path string) ([]byte, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("读取文件失败: %w", err)
    }
    return data, nil
}
  1. 性能优化
  • 对于频繁调用的函数,考虑使用//go:noinline防止内联
  • 避免在热路径中创建闭包
  • 对于大对象,使用指针传递
  1. 文档注释
go 复制代码
// CalculateSum 计算整数切片中所有元素的和
// 参数:
//   numbers: 要相加的整数切片
// 返回值:
//   所有元素的和
// 示例:
//   sum := CalculateSum([]int{1, 2, 3}) // 返回6
func CalculateSum(numbers []int) int {
    sum := 0
    for _, num := range numbers {
        sum += num
    }
    return sum
}
  1. 测试
go 复制代码
func TestCalculateSum(t *testing.T) {
    tests := []struct {
        input    []int
        expected int
    }{
        {[]int{1, 2, 3}, 6},
        {[]int{-1, 1}, 0},
        {[]int{}, 0},
    }
    for _, tt := range tests {
        if got := CalculateSum(tt.input); got != tt.expected {
            t.Errorf("CalculateSum(%v) = %d, want %d", tt.input, got, tt.expected)
        }
    }
}
附录:函数部分习题

注意:如果与上面的练习题在一个包下的时候,会报错,因为有相同的方法名,需要先把上面的那个相同方法给注释掉,才不会报错

scss 复制代码
package main

import (
    "fmt"
    "strings"
    "time"
)

// ============ 练习1: 函数定义 ============
func functionDefinition() {
    fmt.Println("=== 函数定义 ===")

    // 1. 无参数无返回值
    greet()

    // 2. 两数相加
    sum := add(3, 5)
    fmt.Println("3+5 =", sum)

    // 3. 创建点
    createPoint(10, 20, "Origin")

    // 4. 私有函数
    privateFunc()
}

func greet() {
    fmt.Println("Hello, Go!")
}

func add(a, b int) int {
    return a + b
}

func createPoint(x, y int, name string) {
    fmt.Printf("Point %s: (%d, %d)\n", name, x, y)
}

func privateFunc() {
    fmt.Println("This is private")
}

// ============ 练习2: 函数调用 ============
type Calculator struct{}

func functionCall() {
    fmt.Println("\n=== 函数调用 ===")

    // 1. 忽略返回值
    fmt.Println("Ignoring return value")

    // 2. 链式调用
    result := processString("  hello  ")
    fmt.Println("Processed string:", result)

    // 3. 方法调用
    calc := Calculator{}
    product := calc.Multiply(3, 4)
    fmt.Println("3*4 =", product)
}

func processString(s string) string {
    return strings.ToUpper(strings.TrimSpace(s))
}

func (c Calculator) Multiply(a, b int) int {
    return a * b
}

// ============ 练习3: 不定参数 ============
func variadic() {
    fmt.Println("\n=== 不定参数 ===")

    // 1. 不定参数求和
    fmt.Println("Sum of 1,2,3:", sum(1, 2, 3))

    // 2. 混合参数
    printInfo("Numbers", 10, 20, 30)

    // 3. 切片作为不定参数
    nums := []int{1, 2, 3}
    fmt.Println("Sum of slice:", sum(nums...))
}

func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
       total += num
    }
    return total
}

func printInfo(prefix string, values ...int) {
    fmt.Print(prefix, ": ")
    for _, v := range values {
       fmt.Print(v, " ")
    }
    fmt.Println()
}

// ============ 练习4: 多返回值 ============
func multipleReturns() {
    fmt.Println("\n=== 多返回值 ===")

    // 1. 错误处理
    result, err := divide(10, 3)
    if err != nil {
       fmt.Println("Error:", err)
    } else {
       fmt.Println("10/3 =", result)
    }

    // 2. 命名返回值
    min, max := minMax([]int{3, 1, 4, 1, 5})
    fmt.Printf("Min: %d, Max: %d\n", min, max)

    // 3. 成功标志
    quotient, ok := safeDivide(10, 3)
    if ok {
       fmt.Println("10/3 =", quotient)
    }
}

func divide(a, b float64) (float64, error) {
    if b == 0 {
       return 0, fmt.Errorf("division by zero")
    }
    return a / b, nil
}

func minMax(numbers []int) (min, max int) {
    if len(numbers) == 0 {
       return
    }
    min, max = numbers[0], numbers[0]
    for _, num := range numbers {
       if num < min {
          min = num
       }
       if num > max {
          max = num
       }
    }
    return
}

func safeDivide(a, b int) (int, bool) {
    if b == 0 {
       return 0, false
    }
    return a / b, true
}

// ============ 练习5: 匿名函数与闭包 ============
func closures() {
    fmt.Println("\n=== 匿名函数与闭包 ===")

    // 1. 立即调用
    func() {
       fmt.Println("Anonymous function")
    }()

    // 2. 变量赋值
    greet := func(name string) {
       fmt.Printf("Hello, %s!\n", name)
    }
    greet("Alice")

    // 3. 计数器闭包
    counter := createCounter()
    fmt.Println(counter()) // 1
    fmt.Println(counter()) // 2

    // 4. 修复闭包陷阱
    var funcs []func()
    for i := 0; i < 3; i++ {
       i := i // 创建局部变量副本
       funcs = append(funcs, func() {
          fmt.Println(i)
       })
    }
    for _, f := range funcs {
       f() // 输出0,1,2
    }
}

func createCounter() func() int {
    count := 0
    return func() int {
       count++
       return count
    }
}

// ============ 练习6: 综合应用 ============
func advanced() {
    fmt.Println("\n=== 综合应用 ===")

    // 1. 装饰器
    slowFunc := func() {
       time.Sleep(100 * time.Millisecond)
    }
    loggedFunc := logDuration(slowFunc)
    loggedFunc()

    // 2. 函数工厂
    double := createMultiplier(2)
    triple := createMultiplier(3)
    fmt.Println("Double 5:", double(5)) // 10
    fmt.Println("Triple 5:", triple(5)) // 15

    // 3. 私有状态
    account := createBankAccount(100)
    fmt.Println("Balance:", account.balance)
    account.deposit(50)
    fmt.Println("After deposit:", account.balance)
    fmt.Println("Withdraw 30:", account.withdraw(30))
}

func logDuration(f func()) func() {
    return func() {
       start := time.Now()
       f()
       fmt.Printf("Duration: %v\n", time.Since(start))
    }
}

func createMultiplier(factor int) func(int) int {
    return func(x int) int {
       return x * factor
    }
}

func createBankAccount(initial int) *BankAccount {
    balance := initial
    return &BankAccount{
       balance: balance,
       deposit: func(amount int) { balance += amount },
       withdraw: func(amount int) bool {
          if balance >= amount {
             balance -= amount
             return true
          }
          return false
       },
    }
}

type BankAccount struct {
    balance  int
    deposit  func(int)
    withdraw func(int) bool
}

// ============ 主函数 ============
func main() {
    fmt.Println("Go语言函数练习题")
    fmt.Println("================")

    functionDefinition()
    functionCall()
    variadic()
    multipleReturns()
    closures()
    advanced()

    fmt.Println("\n================")
    fmt.Println("练习完成!检查所有TODO部分并运行验证。")
}

1.6 错误处理

Go语言采用独特的错误处理机制,强调显式错误检查和处理。

1.6.1. error 接口

基本定义

go 复制代码
type error interface {
    Error() string
}

关键特性

1.1 创建错误
go 复制代码
package main

import (
    "errors"
    "fmt"
)

// 使用errors.New创建简单错误
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

// 使用fmt.Errorf创建带格式的错误
func validateAge(age int) error {
    if age < 0 {
        return fmt.Errorf("invalid age: %d (must be positive)", age)
    }
    if age > 150 {
        return fmt.Errorf("invalid age: %d (must be <= 150)", age)
    }
    return nil
}
1.2 错误检查
go 复制代码
func main() {
    result, err := divide(10, 0)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    fmt.Printf("Result: %f\n", result)

    // 检查特定错误
    if err := validateAge(-5); err != nil {
        if errors.Is(err, errors.New("invalid age")) { // 简单示例
            fmt.Println("Age validation failed")
        }
        fmt.Println("Error details:", err)
    }
}
1.3 错误包装 (Go 1.13+)
go 复制代码
import "errors"

// 使用%w包装错误
func readFile(path string) ([]byte, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("read file %s failed: %w", path, err)
    }
    return data, nil
}

func main() {
    _, err := readFile("nonexistent.txt")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        
        // 检查底层错误
        if errors.Is(err, os.ErrNotExist) {
            fmt.Println("File does not exist")
        }
        
        // 展开错误链
        fmt.Printf("Unwrapped: %+v\n", errors.Unwrap(err))
    }
}
1.4 自定义错误类型
go 复制代码
type MyError struct {
    Code    int
    Message string
    When    time.Time
}

func (e *MyError) Error() string {
    return fmt.Sprintf("[%s] Error %d: %s", e.When.Format("2006-01-02 15:04:05"), e.Code, e.Message)
}

func (e *MyError) Is(target error) bool {
    t, ok := target.(*MyError)
    if !ok {
        return false
    }
    return e.Code == t.Code
}

func processData(data []byte) error {
    if len(data) == 0 {
        return &MyError{
            Code:    1001,
            Message: "empty data",
            When:    time.Now(),
        }
    }
    return nil
}

func main() {
    if err := processData([]byte{}); err != nil {
        var myErr *MyError
        if errors.As(err, &myErr) {
            fmt.Printf("Custom error: Code=%d, Msg=%s\n", myErr.Code, myErr.Message)
        }
    }
}
1.6.2. defer

基本语法

go 复制代码
defer functionCall()

关键特性

2.1 资源清理
go 复制代码
package main

import (
    "fmt"
    "os"
)

func main() {
    // 文件操作
    file, err := os.Open("data.txt")
    if err != nil {
        fmt.Printf("Failed to open file: %v\n", err)
        return
    }
    defer file.Close() // 确保文件关闭

    // 读取文件内容
    data := make([]byte, 1024)
    _, err = file.Read(data)
    if err != nil {
        fmt.Printf("Read error: %v\n", err)
        return
    }
    fmt.Printf("Read %d bytes\n", len(data))
}
2.2 执行顺序
go 复制代码
func main() {
    fmt.Println("Start")
    
    defer fmt.Println("Deferred 1") // 最后执行
    defer fmt.Println("Deferred 2") // 倒数第二执行
    
    fmt.Println("End")
}
// 输出:
// Start
// End
// Deferred 2
// Deferred 1
2.3 参数求值时机
go 复制代码
func main() {
    i := 1
    defer fmt.Println("Deferred value:", i) // 输出1
    i = 2
    fmt.Println("Current value:", i) // 输出2
}
// 输出:
// Current value: 2
// Deferred value: 1
2.4 常见用途
go 复制代码
// 1. 锁的释放
mu.Lock()
defer mu.Unlock()

// 2. 数据库连接关闭
db, err := sql.Open("mysql", dsn)
if err != nil {
    return err
}
defer db.Close()

// 3. 事务回滚/提交
tx, err := db.Begin()
if err != nil {
    return err
}
defer func() {
    if err != nil {
        tx.Rollback()
    } else {
        tx.Commit()
    }
}()

// 4. 性能监控
start := time.Now()
defer func() {
    fmt.Printf("Execution time: %v\n", time.Since(start))
}()

// 5. 恢复panic
defer func() {
    if r := recover(); r != nil {
        fmt.Printf("Recovered from panic: %v\n", r)
    }
}()
1.6.3. panic() 和 recover()
3.1 panic()

基本用法

go 复制代码
package main

import "fmt"

func main() {
    fmt.Println("Before panic")
    panic("something went wrong")
    fmt.Println("After panic") // 不会执行
}

panic的特性

  • 立即停止当前函数执行
  • 向上传播到调用栈,直到被recover捕获或程序终止
  • 可以传递任何类型的值(通常是字符串或错误)
go 复制代码
// 传递复杂错误
panic(fmt.Errorf("detailed error: %v", err))

// 传递自定义类型
type MyPanic struct{ Message string }
panic(&MyPanic{"custom panic"})
3.2 recover()

基本用法

go 复制代码
package main

import "fmt"

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("Recovered from panic: %v\n", r)
        }
    }()
    
    fmt.Println("Before panic")
    panic("test panic")
    fmt.Println("After panic")
}
// 输出:
// Before panic
// Recovered from panic: test panic

recover的特性

  • 只能在defer函数中调用
  • 返回panic传递的值
  • 如果当前goroutine没有panic,recover返回nil
go 复制代码
func safeCall() {
    defer func() {
        if r := recover(); r != nil {
            switch v := r.(type) {
            case string:
                fmt.Printf("Panic with string: %s\n", v)
            case error:
                fmt.Printf("Panic with error: %v\n", v)
            default:
                fmt.Printf("Panic with unknown type: %v\n", v)
            }
        }
    }()
    
    // 可能panic的代码
    riskyOperation()
}
3.3 典型应用场景

3.3.1 库函数中的panic

go 复制代码
// 在库函数中,当遇到不可恢复的错误时
func MustCompile(str string) *Regexp {
    re, err := Compile(str)
    if err != nil {
        panic(`regexp: Compile(` + quote(str) + `): ` + err.Error())
    }
    return re
}

3.3.2 程序启动时的验证

go 复制代码
func main() {
    // 验证配置
    if config.Port == 0 {
        panic("port must be specified")
    }
    
    // 验证依赖
    if db == nil {
        panic("database connection is nil")
    }
    
    // 正常业务逻辑
    startServer()
}

3.3.3 测试框架

go 复制代码
func TestSomething(t *testing.T) {
    defer func() {
        if r := recover(); r != nil {
            t.Errorf("Unexpected panic: %v", r)
        }
    }()
    
    // 测试代码
    result := functionThatShouldNotPanic()
    if result != expected {
        t.Errorf("Expected %v, got %v", expected, result)
    }
}

3.3.4 防御性编程

go 复制代码
func assert(condition bool, msg string) {
    if !condition {
        panic("Assertion failed: " + msg)
    }
}

func processData(data []byte) {
    assert(len(data) > 0, "data must not be empty")
    assert(len(data) < 1024, "data must be less than 1KB")
    // 处理数据
}

注意:

  1. 错误优先原则
go 复制代码
// 好:优先检查错误
data, err := ioutil.ReadFile("file.txt")
if err != nil {
    return fmt.Errorf("read file failed: %w", err)
}
// 处理数据

// 避免:先使用数据再检查错误
data, _ := ioutil.ReadFile("file.txt")
// 错误处理在后面
  1. 错误包装
go 复制代码
// Go 1.13+
if err := doSomething(); err != nil {
    return fmt.Errorf("doing something failed: %w", err)
}

// 检查特定错误
if errors.Is(err, os.ErrNotExist) {
    // 处理文件不存在
}

// 类型检查
var pathErr *os.PathError
if errors.As(err, &pathErr) {
    // 处理路径错误
}
  1. defer的合理使用
go 复制代码
// 好:在资源获取后立即defer
file, err := os.Open("file.txt")
if err != nil {
    return err
}
defer file.Close()

// 避免:延迟defer
file, err := os.Open("file.txt")
if err != nil {
    return err
}
// ... 其他代码
defer file.Close() // 如果中间有panic,可能无法执行
  1. panic/recover的谨慎使用
go 复制代码
// 好:在程序初始化或库函数中使用
func LoadConfig(path string) *Config {
    data, err := os.ReadFile(path)
    if err != nil {
        panic(fmt.Sprintf("failed to load config: %v", err))
    }
    // 解析配置
    return config
}

// 好:在web服务器中恢复panic
func handleRequest(w http.ResponseWriter, r *http.Request) {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("Recovered from panic: %v", r)
            http.Error(w, "Internal Server Error", 500)
        }
    }()
    // 处理请求
}

// 避免:在业务逻辑中使用panic
func calculate(a, b int) int {
    if b == 0 {
        panic("division by zero") // 应该返回错误
    }
    return a / b
}
  1. 错误处理模式
go 复制代码
// 模式1:直接返回
func process() error {
    data, err := readData()
    if err != nil {
        return err
    }
    return transform(data)
}

// 模式2:添加上下文
func process() error {
    data, err := readData()
    if err != nil {
        return fmt.Errorf("read data failed: %w", err)
    }
    if err := transform(data); err != nil {
        return fmt.Errorf("transform failed: %w", err)
    }
    return nil
}

// 模式3:使用defer处理资源
func processFile(path string) error {
    file, err := os.Open(path)
    if err != nil {
        return err
    }
    defer file.Close()
    
    // 处理文件
    return nil
}
  1. 性能考虑
  • error:零成本,推荐使用
  • panic/recover:有性能开销,应谨慎使用
  • defer:有轻微开销,但现代Go优化得很好
go 复制代码
// 性能敏感代码避免defer
func hotPath() {
    // 直接手动管理资源
    mu.Lock()
    // ... 临界区代码
    mu.Unlock()
}

// 非性能敏感代码使用defer
func regularFunction() {
    mu.Lock()
    defer mu.Unlock()
    // ... 代码
}

常见陷阱与解决方案

  1. defer在循环中的问题
go 复制代码
// 错误:在循环中defer会导致资源延迟释放
func badExample() {
    for i := 0; i < 100000; i++ {
        f, err := os.Create(fmt.Sprintf("file%d.txt", i))
        if err != nil {
            continue
        }
        defer f.Close() // 文件不会立即关闭,可能导致资源耗尽
        f.WriteString("data")
    }
}

// 正确:在循环内部分函数中使用defer
func goodExample() {
    for i := 0; i < 100000; i++ {
        if err := createFile(fmt.Sprintf("file%d.txt", i)); err != nil {
            log.Printf("Failed to create file: %v", err)
        }
    }
}

func createFile(path string) error {
    f, err := os.Create(path)
    if err != nil {
        return err
    }
    defer f.Close()
    _, err = f.WriteString("data")
    return err
}
  1. recover未在defer中调用
go 复制代码
// 错误:recover不在defer中
func badRecover() {
    if r := recover(); r != nil { // 永远不会捕获panic
        fmt.Println("Recovered")
    }
    panic("test")
}

// 正确:在defer中recover
func goodRecover() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered")
        }
    }()
    panic("test")
}
  1. panic传播
go 复制代码
// panic会传播到调用栈
func level3() {
    panic("level3 panic")
}

func level2() {
    level3()
}

func level1() {
    level2()
}

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("Recovered: %v\n", r)
        }
    }()
    level1()
}
  1. defer中的错误处理
go 复制代码
// 错误:忽略defer中的错误
func badDefer() error {
    file, err := os.Create("file.txt")
    if err != nil {
        return err
    }
    defer file.Close() // 忽略关闭错误
    
    _, err = file.WriteString("data")
    return err
}

// 正确:处理defer中的错误
func goodDefer() (resultErr error) {
    file, err := os.Create("file.txt")
    if err != nil {
        return err
    }
    defer func() {
        if err := file.Close(); err != nil && resultErr == nil {
            resultErr = err
        }
    }()
    
    _, resultErr = file.WriteString("data")
    return
}
附录:错误处理部分习题

注意:如果与上面的练习题在一个包下的时候,会报错,因为有相同的方法名,需要先把上面的那个相同方法给注释掉,才不会报错

scss 复制代码
package main

import (
    "errors"
    "fmt"
    "log"
    "net/http"
    "os"
    "time"
)

// ============ 练习1: error接口 ============
func errorInterface() {
    fmt.Println("=== error接口 ===")

    // 1. 创建简单错误
    err := validateInput(-5)
    if err != nil {
       fmt.Println("Error:", err)
    }

    // 2. 带格式的错误
    age := 200
    if err := checkAge(age); err != nil {
       fmt.Println("Age error:", err)
    }

    // 3. 自定义错误类型
    if err := validateData("", ""); err != nil {
       var verr *ValidationError
       if errors.As(err, &verr) {
          fmt.Printf("Validation failed: %s.%s\n", verr.Field, verr.Message)
       }
    }

    // 4. 错误检查
    _, err = os.Stat("nonexistent.txt")
    if errors.Is(err, os.ErrNotExist) {
       fmt.Println("File does not exist")
    }
}

var ErrInvalidInput = errors.New("invalid input")

func validateInput(value int) error {
    if value < 0 {
       return ErrInvalidInput
    }
    return nil
}

func checkAge(age int) error {
    if age < 0 || age > 150 {
       return fmt.Errorf("invalid age: %d", age)
    }
    return nil
}

type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation error for %s: %s", e.Field, e.Message)
}

func validateData(field1, field2 string) error {
    if field1 == "" {
       return &ValidationError{Field: "field1", Message: "cannot be empty"}
    }
    if field2 == "" {
       return &ValidationError{Field: "field2", Message: "cannot be empty"}
    }
    return nil
}

// ============ 练习2: defer ============
func deferStatements() {
    fmt.Println("\n=== defer ===")

    // 1. 文件操作
    readFile("data.txt")

    // 2. 统计执行时间
    slowOperation()

    // 3. 修复循环中的defer
    fixDeferInLoop()

    // 4. 事务处理
    simulateTransaction()
}

func readFile(path string) error {
    file, err := os.Open(path)
    if err != nil {
       return err
    }
    defer file.Close() // 确保关闭

    data := make([]byte, 1024)
    _, err = file.Read(data)
    return err
}

func slowOperation() {
    start := time.Now()
    defer func() {
       fmt.Printf("Operation took %v\n", time.Since(start))
    }()
    time.Sleep(100 * time.Millisecond)
}

func fixDeferInLoop() {
    for i := 0; i < 3; i++ {
       if err := createFile(fmt.Sprintf("file%d.txt", i)); err != nil {
          fmt.Printf("Error creating file: %v\n", err)
       }
    }
}

func createFile(path string) error {
    f, err := os.Create(path)
    if err != nil {
       return err
    }
    defer f.Close() // 在函数内defer
    _, err = f.WriteString("data")
    return err
}

func simulateTransaction() {
    var err error
    tx, err := beginTx()
    if err != nil {
       fmt.Println("Transaction failed to start:", err)
       return
    }
    defer func() {
       if err != nil {
          tx.Rollback()
       } else {
          tx.Commit()
       }
    }()

    // 业务逻辑
    err = doBusinessLogic()
}

type Transaction struct{}

func beginTx() (*Transaction, error) { return &Transaction{}, nil }
func (t *Transaction) Commit()       { fmt.Println("Committed") }
func (t *Transaction) Rollback()     { fmt.Println("Rolled back") }
func doBusinessLogic() error         { return nil }

// ============ 练习3: panic和recover ============
func panicAndRecover() {
    fmt.Println("\n=== panic/recover ===")

    // 1. 不可恢复错误
    safeProcess(nil)

    // 2. 捕获panic
    result, err := callWithRecover()
    if err != nil {
       fmt.Println("Recovered error:", err)
    }
    fmt.Println("Result:", result)
    // 3. HTTP处理器
    http.HandleFunc("/test", handleRequest)

    // 4. 断言函数
    assert(true, "should not panic")
    assert(false, "should panic") // 此行应触发panic
}

func safeProcess(data *int) {
    if data == nil {
       panic("nil pointer dereference")
    }
    fmt.Println("Processing:", *data)
}

func callWithRecover() (result int, err error) {
    defer func() {
       if r := recover(); r != nil {
          err = fmt.Errorf("recovered from panic: %v", r)
       }
    }()

    result = riskyCalculation()
    return
}

func riskyCalculation() int {
    panic("calculation failed")
}

func handleRequest(w http.ResponseWriter, r *http.Request) {
    defer func() {
       if r := recover(); r != nil {
          log.Printf("Recovered from panic: %v", r)
          http.Error(w, "Internal Server Error", 500)
       }
    }()

    riskyOperation()
}

func riskyOperation() {
    panic("server error")
}

func assert(condition bool, msg string) {
    if !condition {
       panic("Assertion failed: " + msg)
    }
}

// ============ 练习4: 综合应用 ============
func advanced() {
    fmt.Println("\n=== 综合应用 ===")

    // 1. 错误包装
    if err := readAndProcess("data.json"); err != nil {
       fmt.Printf("Final error: %+v\n", err)
    }

    // 2. 数据库事务
    db := NewDatabase()
    db.RunTransaction()

    // 3. 防御性panic
    data := MustParseJSON(`{"name": "Alice", "age": 30}`)
    fmt.Println("Parsed:", data)
}

func readAndProcess(path string) error {
    data, err := os.ReadFile(path)
    if err != nil {
       return fmt.Errorf("read file failed: %w", err)
    }

    var result map[string]interface{}
    if err := unmarshal(data, &result); err != nil {
       return fmt.Errorf("unmarshal failed: %w", err)
    }

    // 处理数据
    if _, ok := result["name"]; !ok {
       return fmt.Errorf("missing required field: %w", errors.New("name"))
    }

    return nil
}

func unmarshal(data []byte, v interface{}) error {
    // 模拟JSON解析
    return nil
}

type Database struct{}

func NewDatabase() *Database { return &Database{} }

func (db *Database) BeginTx()  { fmt.Println("Transaction started") }
func (db *Database) Commit()   { fmt.Println("Transaction committed") }
func (db *Database) Rollback() { fmt.Println("Transaction rolled back") }

func (db *Database) RunTransaction() {
    db.BeginTx()

    var resultErr error
    defer func() {
       if resultErr != nil {
          db.Rollback()
       } else {
          db.Commit()
       }
    }()

    // 业务逻辑
    resultErr = db.doWork()
}

func (db *Database) doWork() error { return nil }

func MustParseJSON(data string) map[string]interface{} {
    var result map[string]interface{}
    if err := unmarshal([]byte(data), &result); err != nil {
       panic(fmt.Sprintf("JSON parse failed: %v", err))
    }
    return result
}

// ============ 主函数 ============
func main() {
    fmt.Println("Go语言错误处理练习题")
    fmt.Println("==================")

    errorInterface()
    deferStatements()
    panicAndRecover()
    advanced()

    fmt.Println("\n==================")
    fmt.Println("练习完成!检查所有TODO部分并运行验证。")
}
相关推荐
Ka1Yan4 小时前
[算法] 双指针:本质是“分治思维“——从基础原理到实战的深度解析
java·开发语言·数据结构·算法·面试
追逐时光者5 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 52 期(2025年8.25-8.31)
后端·.net
轮子大叔5 小时前
Spark学习记录
java·spark
一语长情5 小时前
RocketMQ 消息队列冷读问题的分析与优化
java·后端·架构
smilejingwei5 小时前
数据分析编程第六步:大数据运算
java·大数据·开发语言·数据分析·编程·esprocspl
IT_陈寒6 小时前
SpringBoot性能翻倍秘籍:5个90%开发者不知道的JVM调优实战技巧
前端·人工智能·后端
努力努力再努力wz6 小时前
【c++进阶系列】:万字详解异常
java·linux·运维·服务器·开发语言·c++
工一木子6 小时前
Java多线程基础:进程、线程与线程安全实战
java·多线程