Go 语言完整入门指南

2026/07/014 分钟阅读1,409

Go(又称 Golang)是 Google 开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。本指南旨在为初学者提供一套系统、完整的 Go 语言入门教程,涵盖从环境搭建、基本语法、控制流,到结构体、接口、错误处理及并发编程等核心内容。


1. Go 语言简介与环境搭建

1.1 核心特点

  • 简单易学:语法极其精简,只有 25 个关键字。
  • 高并发支持:原生支持协程(Goroutine)和通道(Channel),并发编程心智负担低。
  • 编译型语言:直接编译成机器码,执行效率极高,且部署时只需分发单个二进制文件。
  • 内置垃圾回收 (GC):自动管理内存,避免手动释放的风险。

1.2 环境搭建

  1. Go 官方网站 下载对应系统的安装包安装,或使用包管理器(如 macOS 下运行 brew install go)。
  2. 安装完成后,在终端运行以下命令验证:
    go version

1.3 编写第一个程序 (Hello World)

  1. 创建一个新目录并进入:
    mkdir hello-go && cd hello-go
  2. 初始化 Go 模块(用于管理依赖包):
    go mod init hello-go
  3. 创建名为 main.go 的文件,写入以下内容:
    package main
    
    import "fmt"
    
    func main() {
        fmt.Println("Hello, World!")
    }
  4. 运行程序:
    go run main.go
  5. 编译程序:
    go build main.go
    ./main-go  # 运行编译生成的二进制文件

2. 变量、常量与基本数据类型

2.1 变量声明

Go 提供了多种变量声明方式:

package main

import "fmt"

func main() {
    // 1. 声明并指定类型
    var a int = 10

    // 2. 声明并自动推导类型
    var b = "Golang"

    // 3. 简短变量声明(最常用,只能在函数体内使用)
    c := true

    // 4. 批量声明
    var (
        x int      = 1
        y string   = "hello"
        z float64  = 3.14
    )

    fmt.Println(a, b, c, x, y, z)
}

2.2 常量

使用 const 声明常量,常量的值在编译期确定且不可修改。Go 提供了 iota 关键字用于快速生成递增的常量:

const PI = 3.14159

const (
    Sunday = iota // 0
    Monday        // 1
    Tuesday       // 2
)

2.3 基本数据类型

  • 布尔型bool (值为 truefalse)
  • 整型:有符号(int, int8, int16, int32, int64)和无符号(uint, uint8, uint16, uint32, uint64)。其中 byteuint8 的别名,runeint32 的别名(用于表示一个 Unicode 字符)。
  • 浮点型float32, float64
  • 复数型complex64, complex128
  • 字符串string (双引号表示解释型字符串,反引号 ` 表示原生多行字符串)

3. 容器类型:数组、切片与映射

3.1 数组 (Array)

数组是长度固定且类型相同的元素序列。在 Go 中,数组的长度是其类型的一部分(如 [5]int[10]int 是不同类型)。

var numbers [5]int // 默认初始化为 [0, 0, 0, 0, 0]
numbers[0] = 10

primes := [3]int{2, 3, 5}

3.2 切片 (Slice)

切片是对数组一个连续片段的引用,长度动态可变。在实际开发中,切片的使用率远高于数组。

// 1. 基于数组创建切片
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4] // 包含 arr[1], arr[2], arr[3],即 [2, 3, 4]

// 2. 使用 make 函数创建切片 (长度为 3, 容量为 5)
s := make([]int, 3, 5)

// 3. 追加元素 (当容量不足时,切片会自动扩容)
s = append(s, 100)

3.3 映射 (Map)

映射是一种无序的键值对(Key-Value)集合,键的类型必须支持比较运算符。

// 1. 创建 Map (必须初始化后才能使用)
userAges := make(map[string]int)
userAges["Alice"] = 25

// 2. 字面量创建
scores := map[string]int{
    "Math":    95,
    "English": 88,
}

// 3. 查询键是否存在
score, exists := scores["Math"]
if exists {
    fmt.Println("分数是:", score)
}

// 4. 删除键值对
delete(scores, "English")

4. 控制结构

4.1 条件语句 (if-else)

Go 的 if 条件不需要括号,且支持在条件判断前执行一条初始化语句:

if num := 9; num < 0 {
    fmt.Println(num, "是负数")
} else if num < 10 {
    fmt.Println(num, "是个位数")
} else {
    fmt.Println(num, "是多位数")
}

4.2 条件分支 (switch)

Go 的 switch 分支默认不需要写 break,执行完一个分支后会自动退出。如果想继续执行下一个分支,需使用 fallthrough

switch day := 2; day {
case 1:
    fmt.Println("星期一")
case 2:
    fmt.Println("星期二")
default:
    fmt.Println("其他")
}

4.3 循环语句 (for)

Go 语言中只有 for 循环,没有 whiledo-while

  • 标准三段式循环
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }
  • 替代 while 循环
    n := 1
    for n < 5 {
        n++
    }
  • 无限循环
    for {
        // 循环体,需内部 break
        break
    }
  • 遍历容器 (for-range)
    items := []string{"apple", "banana", "orange"}
    for index, value := range items {
        fmt.Printf("索引: %d, 值: %s\n", index, value)
    }

5. 函数与包管理

5.1 函数定义

Go 支持函数多返回值,也支持命名返回值:

// 定义一个计算面积和周长的函数,返回两个值
func calc(width, height float64) (float64, float64) {
    area := width * height
    perimeter := (width + height) * 2
    return area, perimeter
}

// 命名返回值写法
func divide(a, b int) (result int, err error) {
    if b == 0 {
        return 0, fmt.Errorf("除数不能为零")
    }
    result = a / b
    return // 自动返回 result 和 err
}

5.2 包与导出规则 (Capitalization Rule)

  • Go 以文件夹为单位组织包。每个 Go 源文件的第一行必须声明所属的包名(如 package main)。
  • 导出规则:如果结构体、函数、变量、常量的名字以大写字母开头,表示它是公开的(Public),可以被其他包导入访问;如果以小写字母开头,则表示它是私有的(Private),仅在当前包内可见。

6. 指针、结构体与方法

6.1 指针 (Pointer)

指针存储的是变量的内存地址。Go 提供了指针,但不支持指针运算(无法像 C 语言那样对指针进行自增或偏移),这极大地保证了内存安全。

val := 42
p := &val // p 是指向 val 的指针 (& 获取地址)
fmt.Println(*p) // 42 (* 访问地址存储的值)

6.2 结构体 (Struct)

结构体是由一系列类型相同的或不同的成员变量组合而成的集合,用于模拟面向对象中的实体。

type User struct {
    Name  string
    Age   int
    Email string
}

func main() {
    // 实例化结构体
    u := User{
        Name:  "Bob",
        Age:   30,
        Email: "bob@example.com",
    }
    fmt.Println(u.Name)
}

6.3 方法 (Method)

方法是一个包含了接收者的函数。接收者可以是结构体的值或指针。

// 值接收者:修改接收者的属性不会影响原结构体
func (u User) SayHello() {
    fmt.Printf("你好,我是 %s\n", u.Name)
}

// 指针接收者:可以通过方法直接修改原结构体的属性
func (u *User) SetAge(newAge int) {
    u.Age = newAge
}

7. 接口 (Interface)

接口定义了一组行为规范。在 Go 中,接口的实现是隐式的:一个类型只要实现了接口中定义的所有方法,它就自动实现了该接口,无需像其他语言那样显式声明 implements

package main

import "fmt"

// 定义接口
type Speaker interface {
    Speak() string
}

type Dog struct{}
// Dog 实现了 Speak 方法,因此隐式实现了 Speaker 接口
func (d Dog) Speak() string {
    return "汪汪"
}

type Cat struct{}
func (c Cat) Speak() string {
    return "喵喵"
}

func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

func main() {
    MakeSound(Dog{}) // 输出: 汪汪
    MakeSound(Cat{}) // 输出: 喵喵
}

空接口 (interface{}any)

空接口不包含任何方法,因此任何类型都实现了空接口。空接口可以用来保存任意类型的数据:

var obj any // Go 1.18 起,any 是 interface{} 的别名
obj = 42
obj = "hello"

8. 错误处理与延迟执行

8.1 错误处理机制

Go 推荐显式处理错误。函数将可能发生的错误作为最后一个值返回,调用者通过检查 err 是否为 nil 来判断操作是否成功。

func parseData(data string) (int, error) {
    if data == "" {
        return 0, fmt.Errorf("数据不能为空")
    }
    // 解析逻辑...
    return 100, nil
}

8.2 延迟执行 (defer)

defer 语句会将其后跟随的函数延迟到当前函数执行结束(包括因 panic 退出)前再执行。常用于释放锁、关闭连接、关闭文件句柄等清理工作,遵循后进先出(LIFO)的顺序。

func readFile(filename string) {
    file, err := os.Open(filename)
    if err != nil {
        return
    }
    // 保证在 readFile 函数退出前,文件一定会被关闭,避免内存泄露
    defer file.Close()

    // 读取文件的逻辑...
}

8.3 Panic 与 Recover (异常捕获)

  • panic 用于引发运行时恐慌(类似于抛出致命异常),程序会立刻中断执行并向上层冒泡。
  • recover 用于捕获 panic 引起的恐慌,它必须在 defer 函数中调用才有效。
func safeDivide(a, b int) int {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("捕获到恐慌:", r)
        }
    }()
    if b == 0 {
        panic("除数不能为零") // 触发 panic
    }
    return a / b
}

9. 并发编程:Goroutine 与 Channel

Go 并发的核心哲学是:“不要通过共享内存来通信,而要通过通信来共享内存”

9.1 协程 (Goroutine)

协程是 Go 运行时的轻量级线程,由 Go 运行时管理,其创建和切换开销微乎其微。使用 go 关键字即可开启一个协程:

func printMessage() {
    fmt.Println("并发执行中...")
}

func main() {
    go printMessage() // 开启协程并发运行
}