Golang基础教程

从零开始学习Go语言的基础语法和核心概念

第1章:环境安装与配置

1.1 下载Go

访问Go官方网站 golang.org 下载适合你操作系统的Go安装包。Go支持Windows、macOS和Linux系统。

# 验证Go是否安装成功
go version

# 输出示例
go version go1.23.0 linux/amd64

1.2 配置环境变量

Go需要配置两个重要的环境变量:GOROOT和GOPATH。

Linux/macOS:

# 在 ~/.bashrc 或 ~/.zshrc 中添加
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin

Windows:

在系统环境变量中添加GOROOT和GOPATH

1.3 第一个Go程序

创建你的第一个Go程序,经典的"Hello, World!"。

package main

import "fmt"

func main() {
    fmt.Println("Hello, Golang!")
    fmt.Println("欢迎使用Go Master学习平台")
}

运行程序:

# 保存为 main.go
go run main.go

# 或者编译后运行
go build main.go
./main

第2章:基础语法

2.1 变量声明

Go语言提供了多种变量声明方式,包括var关键字和简短变量声明。

package main

import "fmt"

func main() {
    // 1. 使用var关键字声明
    var name string = "Golang"
    var age int = 15
    var isOpen bool = true
    
    // 2. 类型推断
    var language = "Go"
    var version = 1.23
    
    // 3. 简短变量声明(只能在函数内部使用)
    message := "Hello, World!"
    count := 42
    
    // 4. 多变量声明
    var (
        author  string = "Google"
        year    int    = 2009
        license string = "BSD"
    )
    
    fmt.Printf("语言: %s, 版本: %.2f\n", language, version)
    fmt.Printf("作者: %s, 年份: %d\n", author, year)
}

2.2 常量

常量使用const关键字声明,一旦赋值不能修改。

package main

import "fmt"

func main() {
    // 单行常量
    const Pi = 3.14159
    const AppName = "Go Master"
    
    // 多行常量
    const (
        StatusActive   = "active"
        StatusInactive = "inactive"
        StatusPending  = "pending"
    )
    
    // iota枚举
    const (
        Monday = iota
        Tuesday
        Wednesday
        Thursday
        Friday
        Saturday
        Sunday
    )
    
    fmt.Printf("应用名称: %s\n", AppName)
    fmt.Printf("圆周率: %.5f\n", Pi)
    fmt.Printf("星期: %d\n", Wednesday) // 输出: 2
}

2.3 数据类型

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

基本类型:

  • • 整数:int, int8, int16, int32, int64
  • • 无符号整数:uint, uint8, uint16, uint32, uint64
  • • 浮点数:float32, float64
  • • 布尔:bool
  • • 字符串:string
  • • 字符:byte (uint8), rune (int32)

复合类型:

  • • 数组:Array
  • • 切片:Slice
  • • 映射:Map
  • • 结构体:Struct
  • • 接口:Interface
  • • 通道:Channel
package main

import "fmt"

func main() {
    // 整数类型
    var i8 int8 = 127
    var i16 int16 = 32767
    var i32 int32 = 2147483647
    var i64 int64 = 9223372036854775807
    
    // 无符号整数
    var ui8 uint8 = 255
    var ui16 uint16 = 65535
    
    // 浮点数
    var f32 float32 = 3.14
    var f64 float64 = 3.141592653589793
    
    // 布尔类型
    var isTrue bool = true
    var isFalse bool = false
    
    // 字符串
    var str string = "Hello, 世界"
    
    // 字符类型
    var b byte = 'A'  // 等同于 uint8
    var r rune = '中' // 等同于 int32
    
    fmt.Printf("int8: %d, uint8: %d\n", i8, ui8)
    fmt.Printf("float32: %.2f, float64: %.15f\n", f32, f64)
    fmt.Printf("bool: %v, %v\n", isTrue, isFalse)
    fmt.Printf("string: %s\n", str)
    fmt.Printf("byte: %c, rune: %c\n", b, r)
}

第3章:控制结构

3.1 if语句

Go的if语句支持在条件判断前执行简单语句。

package main

import "fmt"

func main() {
    age := 25
    
    // 基本if语句
    if age >= 18 {
        fmt.Println("成年人")
    } else {
        fmt.Println("未成年")
    }
    
    // if-else if-else
    if age < 13 {
        fmt.Println("儿童")
    } else if age < 18 {
        fmt.Println("青少年")
    } else if age < 60 {
        fmt.Println("成年人")
    } else {
        fmt.Println("老年人")
    }
    
    // 在if前执行语句
    if x := 10; x > 5 {
        fmt.Printf("%d 大于 5\n", x)
    }
    
    // 多个条件
    score := 85
    if score >= 90 && score <= 100 {
        fmt.Println("优秀")
    } else if score >= 80 && score < 90 {
        fmt.Println("良好")
    } else if score >= 60 && score < 80 {
        fmt.Println("及格")
    } else {
        fmt.Println("不及格")
    }
}

3.2 for循环

Go只有for循环,没有while循环,但for可以实现各种循环模式。

package main

import "fmt"

func main() {
    // 1. 传统for循环
    for i := 0; i < 5; i++ {
        fmt.Printf("i = %d\n", i)
    }
    
    // 2. 类似while的循环
    j := 0
    for j < 3 {
        fmt.Printf("j = %d\n", j)
        j++
    }
    
    // 3. 无限循环
    counter := 0
    for {
        if counter >= 3 {
            break
        }
        fmt.Printf("counter = %d\n", counter)
        counter++
    }
    
    // 4. range循环(数组、切片、映射)
    numbers := []int{1, 2, 3, 4, 5}
    for index, value := range numbers {
        fmt.Printf("索引: %d, 值: %d\n", index, value)
    }
    
    // 5. 只获取值
    for _, value := range numbers {
        fmt.Printf("值: %d\n", value)
    }
    
    // 6. 映射遍历
    m := map[string]int{"a": 1, "b": 2, "c": 3}
    for key, value := range m {
        fmt.Printf("键: %s, 值: %d\n", key, value)
    }
}

3.3 switch语句

Go的switch语句非常灵活,可以没有表达式,自动break。

package main

import "fmt"

func main() {
    day := "Monday"
    
    // 基本switch
    switch day {
    case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
        fmt.Println("工作日")
    case "Saturday", "Sunday":
        fmt.Println("周末")
    default:
        fmt.Println("无效的日期")
    }
    
    // 没有表达式的switch(相当于if-else)
    score := 85
    switch {
    case score >= 90:
        fmt.Println("优秀")
    case score >= 80:
        fmt.Println("良好")
    case score >= 60:
        fmt.Println("及格")
    default:
        fmt.Println("不及格")
    }
    
    // 带条件的switch
    switch x := 10; {
    case x > 0 && x < 10:
        fmt.Println("个位数正数")
    case x >= 10 && x < 100:
        fmt.Println("两位数正数")
    default:
        fmt.Println("其他数字")
    }
    
    // fallthrough(继续执行下一个case)
    num := 2
    switch num {
    case 1:
        fmt.Println("一")
        fallthrough
    case 2:
        fmt.Println("二")
        fallthrough
    case 3:
        fmt.Println("三")
    default:
        fmt.Println("其他")
    }
}

第4章:函数

4.1 函数定义与调用

函数是Go语言的核心,支持多返回值和命名返回值。

package main

import "fmt"

// 基本函数
func greet(name string) {
    fmt.Printf("Hello, %s!\n", name)
}

// 带返回值的函数
func add(a, b int) int {
    return a + b
}

// 多返回值
func calculate(a, b int) (int, int, int) {
    sum := a + b
    difference := a - b
    product := a * b
    return sum, difference, product
}

// 命名返回值
func divmod(a, b int) (quotient, remainder int) {
    quotient = a / b
    remainder = a % b
    return // 隐式返回
}

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

func main() {
    // 函数调用
    greet("Go开发者")
    
    result := add(10, 20)
    fmt.Printf("10 + 20 = %d\n", result)
    
    sum, diff, prod := calculate(15, 5)
    fmt.Printf("和: %d, 差: %d, 积: %d\n", sum, diff, prod)
    
    q, r := divmod(17, 3)
    fmt.Printf("商: %d, 余数: %d\n", q, r)
    
    total := sum(1, 2, 3, 4, 5)
    fmt.Printf("总和: %d\n", total)
}

4.2 函数作为参数

Go语言支持高阶函数,可以将函数作为参数传递。

package main

import "fmt"

// 定义函数类型
type Operation func(int, int) int

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

// 减法
func subtract(a, b int) int {
    return a - b
}

// 乘法
func multiply(a, b int) int {
    return a * b
}

// 高阶函数
func calculate(a, b int, op Operation) int {
    return op(a, b)
}

// 返回函数的函数
func getOperation(name string) Operation {
    switch name {
    case "add":
        return add
    case "subtract":
        return subtract
    case "multiply":
        return multiply
    default:
        return nil
    }
}

func main() {
    // 直接调用
    result1 := calculate(10, 5, add)
    fmt.Printf("10 + 5 = %d\n", result1)
    
    result2 := calculate(10, 5, subtract)
    fmt.Printf("10 - 5 = %d\n", result2)
    
    // 从函数获取操作
    op := getOperation("multiply")
    if op != nil {
        result3 := calculate(10, 5, op)
        fmt.Printf("10 * 5 = %d\n", result3)
    }
    
    // 匿名函数
    divide := func(a, b int) int {
        return a / b
    }
    
    result4 := calculate(10, 5, divide)
    fmt.Printf("10 / 5 = %d\n", result4)
}

4.3 闭包

闭包是函数和其引用的外部变量的组合。

package main

import "fmt"

// 返回一个闭包
func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

// 记忆函数
func memoize(f func(int) int) func(int) int {
    cache := make(map[int]int)
    return func(n int) int {
        if val, ok := cache[n]; ok {
            return val
        }
        result := f(n)
        cache[n] = result
        return result
    }
}

// 斐波那契数列
func fibonacci(n int) int {
    if n <= 1 {
        return n
    }
    return fibonacci(n-1) + fibonacci(n-2)
}

func main() {
    // 使用闭包
    counter1 := counter()
    fmt.Println(counter1()) // 1
    fmt.Println(counter1()) // 2
    fmt.Println(counter1()) // 3
    
    counter2 := counter()
    fmt.Println(counter2()) // 1
    fmt.Println(counter2()) // 2
    
    // 使用记忆化
    memoFib := memoize(fibonacci)
    
    fmt.Println(memoFib(10))  // 第一次计算
    fmt.Println(memoFib(10))  // 第二次从缓存读取
    fmt.Println(memoFib(20))  // 新的计算
}

第5章:数组与切片

5.1 数组

数组是固定长度的同类型元素序列。

package main

import "fmt"

func main() {
    // 声明数组
    var numbers [5]int
    numbers[0] = 1
    numbers[1] = 2
    numbers[2] = 3
    numbers[3] = 4
    numbers[4] = 5
    
    // 数组字面量
    var fruits = [5]string{"apple", "banana", "orange", "grape", "pear"}
    
    // 让编译器推断长度
    colors := [...]string{"red", "blue", "green", "yellow"}
    
    // 访问数组元素
    fmt.Printf("第一个水果: %s\n", fruits[0])
    fmt.Printf("第三个颜色: %s\n", colors[2])
    
    // 数组长度
    fmt.Printf("数字数组长度: %d\n", len(numbers))
    fmt.Printf("水果数组长度: %d\n", len(fruits))
    
    // 遍历数组
    fmt.Println("所有水果:")
    for i, fruit := range fruits {
        fmt.Printf("  %d: %s\n", i, fruit)
    }
    
    // 多维数组
    var matrix [3][3]int
    matrix[0][0] = 1
    matrix[1][1] = 2
    matrix[2][2] = 3
    
    fmt.Println("矩阵:")
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            fmt.Printf("%d ", matrix[i][j])
        }
        fmt.Println()
    }
}

5.2 切片

切片是动态数组,是Go中最常用的数据结构。

package main

import "fmt"

func main() {
    // 创建切片
    var slice1 []int
    fmt.Printf("空切片: %v, 长度: %d\n", slice1, len(slice1))
    
    // 使用make创建切片
    slice2 := make([]int, 5)        // 长度为5,容量为5
    slice3 := make([]int, 3, 10)    // 长度为3,容量为10
    
    fmt.Printf("slice2: %v, len: %d, cap: %d\n", slice2, len(slice2), cap(slice2))
    fmt.Printf("slice3: %v, len: %d, cap: %d\n", slice3, len(slice3), cap(slice3))
    
    // 切片字面量
    numbers := []int{1, 2, 3, 4, 5}
    fmt.Printf("数字切片: %v\n", numbers)
    
    // 从数组创建切片
    arr := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    slice4 := arr[2:7]  // 从索引2到6
    fmt.Printf("从数组切片: %v\n", slice4)
    
    // 追加元素
    slice5 := []int{1, 2, 3}
    slice5 = append(slice5, 4, 5, 6)
    fmt.Printf("追加后: %v\n", slice5)
    
    // 追加切片
    slice6 := []int{7, 8, 9}
    slice5 = append(slice5, slice6...)
    fmt.Printf("追加切片后: %v\n", slice5)
    
    // 复制切片
    source := []int{1, 2, 3, 4, 5}
    dest := make([]int, len(source))
    copy(dest, source)
    fmt.Printf("复制后: %v\n", dest)
    
    // 删除元素
    slice7 := []int{1, 2, 3, 4, 5}
    // 删除索引为2的元素
    slice7 = append(slice7[:2], slice7[3:]...)
    fmt.Printf("删除元素后: %v\n", slice7)
    
    // 遍历切片
    fmt.Println("遍历切片:")
    for i, val := range numbers {
        fmt.Printf("  索引 %d: %d\n", i, val)
    }
}

5.3 切片操作技巧

掌握切片的常用操作和最佳实践。

package main

import "fmt"

// 过滤切片
func filter(slice []int, predicate func(int) bool) []int {
    var result []int
    for _, val := range slice {
        if predicate(val) {
            result = append(result, val)
        }
    }
    return result
}

// 映射切片
func mapSlice(slice []int, transform func(int) int) []int {
    result := make([]int, len(slice))
    for i, val := range slice {
        result[i] = transform(val)
    }
    return result
}

// 反转切片
func reverse(slice []int) []int {
    result := make([]int, len(slice))
    for i, j := 0, len(slice)-1; i < len(slice); i, j = i+1, j-1 {
        result[i] = slice[j]
    }
    return result
}

func main() {
    numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    
    // 过滤偶数
    evens := filter(numbers, func(n int) bool {
        return n%2 == 0
    })
    fmt.Printf("偶数: %v\n", evens)
    
    // 过滤奇数
    odds := filter(numbers, func(n int) bool {
        return n%2 != 0
    })
    fmt.Printf("奇数: %v\n", odds)
    
    // 映射:所有数字乘以2
    doubled := mapSlice(numbers, func(n int) int {
        return n * 2
    })
    fmt.Printf("乘以2: %v\n", doubled)
    
    // 反转切片
    reversed := reverse(numbers)
    fmt.Printf("反转: %v\n", reversed)
    
    // 切片排序
    unsorted := []int{5, 2, 8, 1, 9, 3}
    // 使用sort包排序
    fmt.Printf("排序前: %v\n", unsorted)
    
    // 注意:需要导入 sort 包
    // import "sort"
    // sort.Ints(unsorted)
    // fmt.Printf("排序后: %v\n", unsorted)
}

第6章:映射(Map)

6.1 Map基础操作

Map是Go语言中的键值对数据结构,类似于其他语言的字典或哈希表。

package main

import "fmt"

func main() {
    // 创建map
    var m1 map[string]int
    fmt.Printf("空map: %v\n", m1)
    
    // 使用make创建map
    m2 := make(map[string]int)
    m3 := make(map[string]int, 10) // 指定初始容量
    
    // Map字面量
    m4 := map[string]int{
        "apple":  5,
        "banana": 3,
        "orange": 8,
    }
    
    fmt.Printf("m2: %v\n", m2)
    fmt.Printf("m4: %v\n", m4)
    
    // 添加和修改元素
    m4["grape"] = 12
    m4["apple"] = 10
    
    fmt.Printf("修改后: %v\n", m4)
    
    // 访问元素
    value := m4["apple"]
    fmt.Printf("苹果数量: %d\n", value)
    
    // 检查键是否存在
    value, exists := m4["pear"]
    if exists {
        fmt.Printf("梨数量: %d\n", value)
    } else {
        fmt.Println("梨不存在")
    }
    
    // 删除元素
    delete(m4, "banana")
    fmt.Printf("删除后: %v\n", m4)
    
    // 获取map长度
    fmt.Printf("Map长度: %d\n", len(m4))
    
    // 遍历map
    fmt.Println("遍历map:")
    for key, value := range m4 {
        fmt.Printf("  %s: %d\n", key, value)
    }
}

6.2 Map高级用法

Map可以存储复杂的数据类型,包括结构体、切片等。

package main

import "fmt"

// 学生信息
type Student struct {
    Name   string
    Age    int
    Grades map[string]float64
}

func main() {
    // Map存储结构体
    students := make(map[string]Student)
    
    students["Alice"] = Student{
        Name: "Alice",
        Age:  20,
        Grades: map[string]float64{
            "Math":    95.5,
            "English": 88.0,
            "Science": 92.5,
        },
    }
    
    students["Bob"] = Student{
        Name: "Bob",
        Age:  21,
        Grades: map[string]float64{
            "Math":    78.5,
            "English": 85.0,
            "Science": 80.0,
        },
    }
    
    // 访问嵌套map
    alice := students["Alice"]
    fmt.Printf("Alice的数学成绩: %.1f\n", alice.Grades["Math"])
    
    // Map存储切片
    phoneBook := make(map[string][]string)
    phoneBook["Alice"] = []string{"123-456", "789-012"}
    phoneBook["Bob"] = []string{"456-789"}
    
    fmt.Printf("Alice的所有电话: %v\n", phoneBook["Alice"])
    
    // Map作为集合使用
    visited := make(map[string]bool)
    cities := []string{"Beijing", "Shanghai", "Beijing", "Guangzhou", "Shanghai"}
    
    for _, city := range cities {
        if !visited[city] {
            visited[city] = true
            fmt.Printf("首次访问: %s\n", city)
        }
    }
    
    // 统计单词频率
    text := "hello world hello go world"
    words := make(map[string]int)
    
    for _, word := range []string{"hello", "world", "hello", "go", "world"} {
        words[word]++
    }
    
    fmt.Println("单词频率:")
    for word, count := range words {
        fmt.Printf("  %s: %d\n", word, count)
    }
}

6.3 Map与JSON

Map与JSON的相互转换是Web开发中的常见操作。

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

func main() {
    // Map转JSON
    data := map[string]interface{}{
        "name":    "Alice",
        "age":     30,
        "email":   "alice@example.com",
        "hobbies": []string{"reading", "swimming", "coding"},
        "address": map[string]string{
            "city":    "Beijing",
            "country": "China",
        },
    }
    
    // 编码为JSON
    jsonData, err := json.Marshal(data)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("JSON: %s\n", jsonData)
    
    // JSON转Map
    var decodedData map[string]interface{}
    err = json.Unmarshal(jsonData, &decodedData)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("解码后的Map: %v\n", decodedData)
    
    // 访问解码后的数据
    fmt.Printf("姓名: %v\n", decodedData["name"])
    fmt.Printf("年龄: %v\n", decodedData["age"])
    
    // 处理嵌套数据
    if address, ok := decodedData["address"].(map[string]interface{}); ok {
        fmt.Printf("城市: %v\n", address["city"])
    }
    
    // 处理数组数据
    if hobbies, ok := decodedData["hobbies"].([]interface{}); ok {
        fmt.Println("爱好:")
        for _, hobby := range hobbies {
            fmt.Printf("  - %v\n", hobby)
        }
    }
}

第7章:结构体

7.1 结构体定义与使用

结构体是Go语言中的自定义数据类型,用于组合多个字段。

package main

import "fmt"

// 定义结构体
type Person struct {
    Name string
    Age  int
}

// 带有更多字段的结构体
type Student struct {
    Name    string
    Age     int
    Grade   float64
    Subjects []string
    Address *Address // 指针字段
}

type Address struct {
    City    string
    Country string
}

func main() {
    // 创建结构体实例
    var p1 Person
    p1.Name = "Alice"
    p1.Age = 25
    
    // 结构体字面量
    p2 := Person{
        Name: "Bob",
        Age:  30,
    }
    
    // 简写形式(按字段顺序)
    p3 := Person{"Charlie", 35}
    
    fmt.Printf("p1: %+v\n", p1)
    fmt.Printf("p2: %+v\n", p2)
    fmt.Printf("p3: %+v\n", p3)
    
    // 访问结构体字段
    fmt.Printf("%s 的年龄是 %d\n", p1.Name, p1.Age)
    
    // 创建复杂结构体
    student := Student{
        Name:  "David",
        Age:   20,
        Grade: 89.5,
        Subjects: []string{"Math", "English", "Science"},
        Address: &Address{
            City:    "Beijing",
            Country: "China",
        },
    }
    
    fmt.Printf("学生信息: %+v\n", student)
    fmt.Printf("城市: %s\n", student.Address.City)
    
    // 结构体比较
    p4 := Person{Name: "Alice", Age: 25}
    if p1 == p4 {
        fmt.Println("p1 和 p4 相等")
    }
    
    // 结构体数组
    people := []Person{
        {Name: "Eve", Age: 28},
        {Name: "Frank", Age: 32},
        {Name: "Grace", Age: 27},
    }
    
    fmt.Println("所有人:")
    for _, person := range people {
        fmt.Printf("  %s (%d岁)\n", person.Name, person.Age)
    }
}

7.2 方法定义

方法是与特定类型关联的函数,可以为任何类型定义方法。

package main

import (
    "fmt"
    "math"
)

// 定义结构体
type Rectangle struct {
    Width  float64
    Height float64
}

type Circle struct {
    Radius float64
}

// 值接收者方法
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

// 指针接收者方法
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

// Circle的方法
func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * math.Pi * c.Radius
}

// 字符串表示方法
func (r Rectangle) String() string {
    return fmt.Sprintf("矩形[宽:%.2f, 高:%.2f]", r.Width, r.Height)
}

func (c Circle) String() string {
    return fmt.Sprintf("圆形[半径:%.2f]", c.Radius)
}

// 自定义类型方法
type MyInt int

func (mi MyInt) IsEven() bool {
    return mi%2 == 0
}

func (mi MyInt) IsPositive() bool {
    return mi > 0
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}
    circle := Circle{Radius: 3}
    
    // 调用方法
    fmt.Printf("%s 面积: %.2f\n", rect, rect.Area())
    fmt.Printf("%s 周长: %.2f\n", rect, rect.Perimeter())
    
    fmt.Printf("%s 面积: %.2f\n", circle, circle.Area())
    fmt.Printf("%s 周长: %.2f\n", circle, circle.Perimeter())
    
    // 修改结构体
    fmt.Printf("缩放前: %s\n", rect)
    rect.Scale(2)
    fmt.Printf("缩放后: %s\n", rect)
    
    // 自定义类型方法
    num := MyInt(42)
    fmt.Printf("%d 是偶数: %v\n", num, num.IsEven())
    fmt.Printf("%d 是正数: %v\n", num, num.IsPositive())
    
    // 方法值和方法表达式
    areaMethod := rect.Area
    fmt.Printf("使用方法值: %.2f\n", areaMethod())
    
    scaleMethod := (*Rectangle).Scale
    scaleMethod(&rect, 0.5)
    fmt.Printf("使用方法表达式: %s\n", rect)
}

7.3 结构体嵌套与组合

Go语言通过结构体嵌套和组合来实现代码复用。

package main

import "fmt"

// 基础结构体
type Person struct {
    Name string
    Age  int
}

// 嵌套结构体
type Employee struct {
    Person     // 匿名字段,继承Person的所有字段和方法
    EmployeeID string
    Department string
    Salary     float64
}

// 显式嵌套
type Student struct {
    Person     Person // 命名字段
    StudentID string
    Major      string
    GPA        float64
}

// 方法定义
func (p Person) GetInfo() string {
    return fmt.Sprintf("%s (%d岁)", p.Name, p.Age)
}

func (e Employee) GetEmployeeInfo() string {
    return fmt.Sprintf("员工 %s, 部门: %s, 薪资: %.2f", 
        e.Name, e.Department, e.Salary)
}

func main() {
    // 匿名字段嵌套
    emp := Employee{
        Person: Person{
            Name: "Alice",
            Age:  30,
        },
        EmployeeID: "E001",
        Department: "Engineering",
        Salary:     80000.0,
    }
    
    // 可以直接访问嵌套字段
    fmt.Printf("员工姓名: %s\n", emp.Name)
    fmt.Printf("员工年龄: %d\n", emp.Age)
    
    // 也可以显式访问
    fmt.Printf("员工姓名: %s\n", emp.Person.Name)
    
    // 调用嵌套结构体的方法
    fmt.Printf("基本信息: %s\n", emp.GetInfo())
    fmt.Printf("员工信息: %s\n", emp.GetEmployeeInfo())
    
    // 命名字段嵌套
    student := Student{
        Person: Person{
            Name: "Bob",
            Age:  22,
        },
        StudentID: "S001",
        Major:     "Computer Science",
        GPA:       3.8,
    }
    
    fmt.Printf("学生姓名: %s\n", student.Person.Name)
    fmt.Printf("专业: %s\n", student.Major)
    
    // 结构体字面量简写
    emp2 := Employee{
        Person: Person{Name: "Charlie", Age: 35},
        EmployeeID: "E002",
        Department: "Marketing",
        Salary:     70000.0,
    }
    
    fmt.Printf("新员工: %s\n", emp2.GetEmployeeInfo())
    
    // 多层嵌套
    type Company struct {
        Name      string
        Employees []Employee
        Address   Address
    }
    
    company := Company{
        Name: "Tech Corp",
        Employees: []Employee{emp, emp2},
        Address: Address{
            City:    "San Francisco",
            Country: "USA",
        },
    }
    
    fmt.Printf("公司名称: %s\n", company.Name)
    fmt.Printf("员工数量: %d\n", len(company.Employees))
    fmt.Printf("总部: %s, %s\n", company.Address.City, company.Address.Country)
}

第8章:接口

8.1 接口基础

接口是Go语言中的类型,它定义了一组方法签名。任何实现了这些方法的类型都实现了该接口。

package main

import "fmt"

// 定义接口
type Shape interface {
    Area() float64
    Perimeter() float64
}

// 定义结构体
type Rectangle struct {
    Width  float64
    Height float64
}

type Circle struct {
    Radius float64
}

// Rectangle实现Shape接口
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

// Circle实现Shape接口
func (c Circle) Area() float64 {
    return 3.14159 * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * 3.14159 * c.Radius
}

// 使用接口的函数
func printShapeInfo(s Shape) {
    fmt.Printf("面积: %.2f\n", s.Area())
    fmt.Printf("周长: %.2f\n", s.Perimeter())
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}
    circle := Circle{Radius: 3}
    
    // 结构体实例作为接口使用
    var shape Shape
    
    shape = rect
    fmt.Println("矩形信息:")
    printShapeInfo(shape)
    
    shape = circle
    fmt.Println("\n圆形信息:")
    printShapeInfo(shape)
    
    // 接口切片
    shapes := []Shape{rect, circle}
    fmt.Println("\n所有形状:")
    for i, shape := range shapes {
        fmt.Printf("形状 %d:\n", i+1)
        printShapeInfo(shape)
        fmt.Println()
    }
    
    // 空接口
    var anything interface{}
    anything = 42
    fmt.Printf("anything = %v (%T)\n", anything, anything)
    
    anything = "Hello"
    fmt.Printf("anything = %v (%T)\n", anything, anything)
    
    anything = Rectangle{Width: 1, Height: 2}
    fmt.Printf("anything = %v (%T)\n", anything, anything)
}

8.2 接口组合与空接口

接口可以组合其他接口,空接口可以表示任何类型。

package main

import "fmt"

// 基础接口
type Writer interface {
    Write([]byte) (int, error)
}

type Reader interface {
    Read([]byte) (int, error)
}

// 组合接口
type ReadWriter interface {
    Reader
    Writer
}

// 空接口
type Empty interface{}

// 实现类型
type File struct {
    Name string
}

func (f File) Read(b []byte) (int, error) {
    fmt.Printf("从文件 %s 读取数据\n", f.Name)
    return len(b), nil
}

func (f File) Write(b []byte) (int, error) {
    fmt.Printf("向文件 %s 写入数据\n", f.Name)
    return len(b), nil
}

// 使用空接口的函数
func printAnything(v interface{}) {
    fmt.Printf("值: %v, 类型: %T\n", v, v)
}

// 类型断言
func checkType(v interface{}) {
    switch val := v.(type) {
    case int:
        fmt.Printf("这是一个整数: %d\n", val)
    case string:
        fmt.Printf("这是一个字符串: %s\n", val)
    case bool:
        fmt.Printf("这是一个布尔值: %v\n", val)
    case Rectangle:
        fmt.Printf("这是一个矩形: %+v\n", val)
    default:
        fmt.Printf("未知类型: %T\n", val)
    }
}

// 类型查询
func describe(i interface{}) {
    // 类型断言
    if str, ok := i.(string); ok {
        fmt.Printf("字符串: %s\n", str)
    }
    
    // 类型switch
    switch v := i.(type) {
    case int, int32, int64:
        fmt.Printf("整数: %d\n", v)
    case float32, float64:
        fmt.Printf("浮点数: %f\n", v)
    case string:
        fmt.Printf("字符串: %s\n", v)
    default:
        fmt.Printf("其他类型: %T\n", v)
    }
}

type Rectangle struct {
    Width  float64
    Height float64
}

func main() {
    file := File{Name: "example.txt"}
    
    // 使用组合接口
    var rw ReadWriter = file
    
    data := []byte("Hello, World!")
    rw.Read(data)
    rw.Write(data)
    
    // 空接口使用
    fmt.Println("\n空接口示例:")
    printAnything(42)
    printAnything("Hello")
    printAnything(true)
    printAnything(Rectangle{Width: 10, Height: 5})
    
    // 类型断言
    fmt.Println("\n类型断言示例:")
    checkType(42)
    checkType("Hello")
    checkType(true)
    checkType(Rectangle{Width: 10, Height: 5})
    
    // 类型查询
    fmt.Println("\n类型查询示例:")
    describe(42)
    describe(3.14)
    describe("Hello")
    describe([]int{1, 2, 3})
}

8.3 类型断言与类型查询

类型断言和类型查询是在运行时检查接口变量具体类型的方法。

package main

import (
    "fmt"
    "strconv"
)

// 类型断言示例
func assertType(i interface{}) {
    // 方式1: 简单断言(可能panic)
    // str := i.(string)
    
    // 方式2: 安全断言
    if str, ok := i.(string); ok {
        fmt.Printf("字符串: %s\n", str)
    } else {
        fmt.Printf("不是字符串,实际类型: %T\n", i)
    }
    
    // 方式3: 类型switch(推荐)
    switch v := i.(type) {
    case string:
        fmt.Printf("字符串类型: %s\n", v)
    case int:
        fmt.Printf("整数类型: %d\n", v)
    case float64:
        fmt.Printf("浮点数类型: %f\n", v)
    case bool:
        fmt.Printf("布尔类型: %v\n", v)
    default:
        fmt.Printf("未知类型: %T\n", v)
    }
}

// 接口转换函数
func convertToString(v interface{}) (string, error) {
    switch val := v.(type) {
    case string:
        return val, nil
    case int:
        return strconv.Itoa(val), nil
    case float64:
        return strconv.FormatFloat(val, 'f', -1, 64), nil
    case bool:
        return strconv.FormatBool(val), nil
    default:
        return "", fmt.Errorf("unsupported type: %T", v)
    }
}

// 接口切片处理
func processSlice(slice []interface{}) {
    for i, item := range slice {
        fmt.Printf("索引 %d: ", i)
        
        switch v := item.(type) {
        case int:
            fmt.Printf("整数 %d 的平方: %d\n", v, v*v)
        case string:
            fmt.Printf("字符串 '%s' 的长度: %d\n", v, len(v))
        case []int:
            sum := 0
            for _, num := range v {
                sum += num
            }
            fmt.Printf("整数数组的和: %d\n", sum)
        default:
            fmt.Printf("未知类型: %T\n", v)
        }
    }
}

func main() {
    // 类型断言示例
    fmt.Println("类型断言示例:")
    assertType("Hello")
    assertType(42)
    assertType(3.14)
    assertType(true)
    
    // 接口转换
    fmt.Println("\n接口转换示例:")
    values := []interface{}{"Hello", 42, 3.14, true}
    for _, v := range values {
        str, err := convertToString(v)
        if err != nil {
            fmt.Printf("转换失败: %v\n", err)
        } else {
            fmt.Printf("转换结果: %s\n", str)
        }
    }
    
    // 处理接口切片
    fmt.Println("\n接口切片处理:")
    mixedSlice := []interface{}{
        1, 2, 3,
        "hello", "world",
        []int{4, 5, 6},
        3.14,
    }
    processSlice(mixedSlice)
    
    // 接口嵌套
    fmt.Println("\n接口嵌套示例:")
    var nested interface{}
    nested = map[string]interface{}{
        "name": "Alice",
        "age":  30,
        "skills": []string{"Go", "JavaScript", "Python"},
    }
    
    if m, ok := nested.(map[string]interface{}); ok {
        fmt.Printf("姓名: %v\n", m["name"])
        if skills, ok := m["skills"].([]interface{}); ok {
            fmt.Printf("技能: %v\n", skills)
        }
    }
}

第9章:错误处理

9.1 Error接口

Go语言使用error接口来表示错误,error是一个内置接口类型。

package main

import (
    "errors"
    "fmt"
    "strconv"
)

// 自定义错误类型
type MyError struct {
    Code    int
    Message string
}

func (e MyError) Error() string {
    return fmt.Sprintf("错误代码 %d: %s", e.Code, e.Message)
}

// 返回错误的函数
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("除数不能为零")
    }
    return a / b, nil
}

// 使用自定义错误
func validateAge(age int) error {
    if age < 0 {
        return MyError{Code: 400, Message: "年龄不能为负数"}
    }
    if age > 150 {
        return MyError{Code: 400, Message: "年龄超出合理范围"}
    }
    return nil
}

// 包装错误
func readConfig() error {
    err := readFile("config.txt")
    if err != nil {
        return fmt.Errorf("读取配置文件失败: %w", err)
    }
    return nil
}

func readFile(filename string) error {
    return errors.New("文件不存在")
}

func main() {
    // 基本错误处理
    result, err := divide(10, 2)
    if err != nil {
        fmt.Printf("错误: %v\n", err)
    } else {
        fmt.Printf("结果: %.2f\n", result)
    }
    
    // 错误处理
    result, err = divide(10, 0)
    if err != nil {
        fmt.Printf("错误: %v\n", err)
    }
    
    // 自定义错误
    err = validateAge(-5)
    if err != nil {
        fmt.Printf("验证错误: %v\n", err)
        
        // 类型断言获取详细信息
        if myErr, ok := err.(MyError); ok {
            fmt.Printf("错误代码: %d\n", myErr.Code)
        }
    }
    
    // 包装错误
    err = readConfig()
    if err != nil {
        fmt.Printf("配置错误: %v\n", err)
        
        // 使用errors.Is和errors.As
        var myErr MyError
        if errors.As(err, &myErr) {
            fmt.Printf("自定义错误: %v\n", myErr)
        }
    }
    
    // 字符串转换错误处理
    str := "123"
    num, err := strconv.Atoi(str)
    if err != nil {
        fmt.Printf("转换错误: %v\n", err)
    } else {
        fmt.Printf("转换结果: %d\n", num)
    }
    
    // 错误的错误处理示例
    str2 := "abc"
    num2, err := strconv.Atoi(str2)
    if err != nil {
        fmt.Printf("转换 '%s' 失败: %v\n", str2, err)
        num2 = 0 // 使用默认值
    }
    fmt.Printf("使用默认值: %d\n", num2)
}

9.2 defer语句

defer语句用于延迟函数的执行,常用于资源清理和错误处理。

package main

import (
    "fmt"
    "os"
)

// defer基础用法
func deferExample() {
    fmt.Println("开始")
    defer fmt.Println("延迟执行1")
    defer fmt.Println("延迟执行2")
    fmt.Println("结束")
    // 输出顺序:开始 -> 结束 -> 延迟执行2 -> 延迟执行1
}

// defer与资源管理
func fileOperation(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer file.Close() // 确保文件在函数结束时关闭
    
    // 处理文件...
    fmt.Printf("处理文件: %s\n", filename)
    return nil
}

// defer与匿名函数
func anonymousDefer() {
    x := 10
    defer func() {
        fmt.Printf("x的值: %d\n", x) // 捕获变量
    }()
    
    x = 20
    fmt.Printf("修改后的x: %d\n", x)
}

// defer参数求值
func deferParameter() {
    x := 10
    defer fmt.Printf("defer x = %d\n", x) // 参数在defer时求值
    x = 20
    fmt.Printf("x = %d\n", x)
}

// 多个defer的执行顺序
func multipleDefer() {
    fmt.Println("函数开始")
    
    for i := 1; i <= 5; i++ {
        defer fmt.Printf("defer %d\n", i)
    }
    
    fmt.Println("函数结束")
    // defer按LIFO顺序执行
}

// defer与返回值
func deferWithReturn() (result int) {
    defer func() {
        result++ // 可以修改命名返回值
    }()
    
    return 10 // 实际返回11
}

// 错误处理模式
func processResource(id string) error {
    resource, err := acquireResource(id)
    if err != nil {
        return fmt.Errorf("获取资源失败: %w", err)
    }
    
    // 使用defer确保资源释放
    defer func() {
        if err := releaseResource(resource); err != nil {
            fmt.Printf("释放资源失败: %v\n", err)
        }
    }()
    
    // 处理资源
    if err := doWork(resource); err != nil {
        return fmt.Errorf("处理资源失败: %w", err)
    }
    
    return nil
}

// 模拟函数
func acquireResource(id string) (string, error) {
    if id == "error" {
        return "", fmt.Errorf("资源不存在")
    }
    return "resource-" + id, nil
}

func releaseResource(resource string) error {
    fmt.Printf("释放资源: %s\n", resource)
    return nil
}

func doWork(resource string) error {
    fmt.Printf("处理资源: %s\n", resource)
    return nil
}

func main() {
    fmt.Println("=== defer基础示例 ===")
    deferExample()
    
    fmt.Println("\n=== defer参数求值 ===")
    deferParameter()
    
    fmt.Println("\n=== 多个defer ===")
    multipleDefer()
    
    fmt.Println("\n=== defer与返回值 ===")
    result := deferWithReturn()
    fmt.Printf("返回值: %d\n", result)
    
    fmt.Println("\n=== 资源管理示例 ===")
    if err := fileOperation("test.txt"); err != nil {
        fmt.Printf("文件操作失败: %v\n", err)
    }
    
    fmt.Println("\n=== 错误处理模式 ===")
    if err := processResource("123"); err != nil {
        fmt.Printf("处理失败: %v\n", err)
    }
    
    if err := processResource("error"); err != nil {
        fmt.Printf("处理失败: %v\n", err)
    }
    
    fmt.Println("\n=== 匿名函数defer ===")
    anonymousDefer()
}

9.3 panic与recover

panic用于抛出异常,recover用于捕获异常,应该谨慎使用。

package main

import "fmt"

// panic示例
func panicExample() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("捕获到panic: %v\n", r)
        }
    }()
    
    fmt.Println("开始执行")
    panic("发生严重错误")
    fmt.Println("这行不会执行")
}

// 安全的除法函数
func safeDivide(a, b float64) (result float64, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("除法运算panic: %v", r)
        }
    }()
    
    if b == 0 {
        panic("除数为零")
    }
    
    result = a / b
    return result, nil
}

// 检查函数
func checkValue(v int) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("检查值时发生panic: %v\n", r)
        }
    }()
    
    if v < 0 {
        panic("值不能为负数")
    }
    if v > 100 {
        panic("值不能大于100")
    }
    
    fmt.Printf("值 %d 有效\n", v)
}

// 资源清理示例
func processWithCleanup() {
    fmt.Println("开始处理")
    
    defer func() {
        fmt.Println("清理资源")
        if r := recover(); r != nil {
            fmt.Printf("处理过程中发生panic: %v\n", r)
            fmt.Println("执行错误恢复逻辑")
        }
    }()
    
    // 模拟处理步骤
    fmt.Println("步骤1: 验证输入")
    fmt.Println("步骤2: 处理数据")
    
    // 模拟错误
    panic("数据处理失败")
    
    fmt.Println("步骤3: 保存结果")
}

// 自定义panic类型
type MyPanic struct {
    Code    int
    Message string
}

func (p MyPanic) String() string {
    return fmt.Sprintf("MyPanic[%d]: %s", p.Code, p.Message)
}

func customPanicExample() {
    defer func() {
        if r := recover(); r != nil {
            switch val := r.(type) {
            case MyPanic:
                fmt.Printf("捕获到自定义panic: %v\n", val)
            case string:
                fmt.Printf("捕获到字符串panic: %s\n", val)
            default:
                fmt.Printf("捕获到未知panic: %v\n", val)
            }
        }
    }()
    
    panic(MyPanic{Code: 500, Message: "内部服务器错误"})
}

func main() {
    fmt.Println("=== panic与recover基础 ===")
    panicExample()
    fmt.Println("程序继续执行")
    
    fmt.Println("\n=== 安全的除法 ===")
    result, err := safeDivide(10, 2)
    if err != nil {
        fmt.Printf("错误: %v\n", err)
    } else {
        fmt.Printf("结果: %.2f\n", result)
    }
    
    result, err = safeDivide(10, 0)
    if err != nil {
        fmt.Printf("错误: %v\n", err)
    }
    
    fmt.Println("\n=== 值检查 ===")
    checkValue(50)
    checkValue(-1)
    checkValue(150)
    
    fmt.Println("\n=== 资源清理 ===")
    processWithCleanup()
    fmt.Println("程序继续执行")
    
    fmt.Println("\n=== 自定义panic ===")
    customPanicExample()
    fmt.Println("程序继续执行")
}

第10章:包管理

10.1 Go模块系统

Go模块是Go 1.11引入的包管理系统,用于管理项目依赖。

# 初始化模块
go mod init github.com/yourusername/myproject

# 查看模块信息
go mod tidy

# 下载依赖
go mod download

# 验证依赖
go mod verify

# 查看依赖图
go mod graph

# 更新依赖
go get -u ./...

# 清理未使用的依赖
go mod tidy
// go.mod 示例
module github.com/yourusername/myproject

go 1.23

require (
    github.com/gin-gonic/gin v1.9.1
    github.com/golang-jwt/jwt/v5 v5.0.0
    gorm.io/driver/mysql v1.5.1
    gorm.io/gorm v1.25.2
)

require (
    // 间接依赖
    github.com/gabriel-vasile/mimetype v1.4.2 // indirect
    github.com/gin-contrib/sse v0.1.0 // indirect
    // ... 更多间接依赖
)

10.2 包的组织与导入

了解如何组织包结构和管理包的导入。

// 项目结构示例
/*
myproject/
├── go.mod
├── go.sum
├── main.go
├── cmd/
│   └── server/
│       └── main.go
├── internal/
│   ├── auth/
│   │   └── jwt.go
│   ├── handlers/
│   │   └── user.go
│   └── models/
│       └── user.go
├── pkg/
│   ├── logger/
│   │   └── logger.go
│   └── utils/
│       └── string.go
└── api/
    └── openapi.yaml
*/

// main.go
package main

import (
    "fmt"
    "log"
    
    "github.com/yourusername/myproject/internal/auth"
    "github.com/yourusername/myproject/internal/handlers"
    "github.com/yourusername/myproject/internal/models"
    "github.com/yourusername/myproject/pkg/logger"
)

func main() {
    // 初始化日志
    logger.Init()
    
    // 初始化数据库
    if err := models.InitDB(); err != nil {
        log.Fatal(err)
    }
    
    // 初始化认证
    auth.Init()
    
    // 启动服务器
    if err := handlers.StartServer(); err != nil {
        log.Fatal(err)
    }
}

// 包的可见性规则
package example

// 导出的标识符(首字母大写)
var PublicVariable = "任何人都可以访问"
const PublicConstant = "公开常量"

func PublicFunction() {
    fmt.Println("这是公开函数")
}

type PublicStruct struct {
    PublicField  string
    privateField string // 私有字段
}

// 未导出的标识符(首字母小写)
var privateVariable = "只能在包内访问"
const privateConstant = "私有常量"

func privateFunction() {
    fmt.Println("这是私有函数")
}

type privateStruct struct {
    field string
}

10.3 依赖管理

管理项目依赖,包括添加、更新和移除依赖。

# 添加依赖
go get github.com/gin-gonic/gin

# 添加特定版本
go get github.com/gin-gonic/gin@v1.9.1

# 添加最新版本
go get github.com/gin-gonic/gin@latest

# 更新依赖
go get -u github.com/gin-gonic/gin

# 更新所有依赖
go get -u ./...

# 移除未使用的依赖
go mod tidy

# 查看依赖树
go mod graph

# 查看为什么需要某个依赖
go mod why github.com/gin-gonic/gin

# 下载依赖到本地缓存
go mod download

# 清理模块缓存
go clean -modcache

# 编辑go.mod
go mod edit -replace github.com/old/package=github.com/new/package@v1.0.0
// 版本管理示例
/*
go.mod中的版本语法:

v1.2.3 - 精确版本
v1.2.x - 补丁版本可变
v1.x.x - 次版本可变
latest - 最新版本

语义化版本:主版本.次版本.补丁版本
v1.0.0 - 初始稳定版本
v1.1.0 - 向后兼容的新功能
v1.1.1 - 向后兼容的bug修复
v2.0.0 - 破坏性变更
*/

// 使用replace指令
module myproject

go 1.23

require (
    github.com/gin-gonic/gin v1.9.1
    // 使用本地版本
)

replace github.com/gin-gonic/gin => ../local/gin

// 使用exclude排除版本
exclude github.com/some/package v1.2.3

// 使用retract撤回版本
retract (
    v1.0.0 // 有严重bug
    v1.0.1 // 向后不兼容
)

10.4 私有模块与代理

配置私有模块和代理服务器,解决依赖下载问题。

# 设置GOPROXY(中国用户推荐)
export GOPROXY=https://goproxy.cn,direct

# 设置私有模块
export GOPRIVATE=*.corp.example.com,github.com/mycompany/*

# 设置GOSUMDB(校验和数据库)
export GOSUMDB=sum.golang.org

# 完全禁用校验和验证(不推荐)
export GOSUMDB=off

# 使用环境变量配置
export GO111MODULE=on
export GOPROXY=https://proxy.golang.org,direct
export GOSUMDB=sum.golang.org

# 在go.mod中配置私有模块
/*
go.mod:
go 1.23

require (
    github.com/gin-gonic/gin v1.9.1
    gitlab.com/private/package v1.0.0
)
*/

# 使用ssh代替https下载私有模块
git config --global url."ssh://git@github.com/".insteadOf "https://github.com/"

# 配置多个代理
export GOPROXY="https://goproxy.cn|https://proxy.golang.org|direct"

# 查看当前配置
go env GOPROXY
go env GOSUMDB
go env GOPRIVATE