# 1. init () 函数概述 🌟

在 Go 语言中, init() 函数是一个特殊的函数,用于执行包级别的初始化操作。它具有独特的执行机制和使用规则,是 Go 语言中重要的特性之一。

# 2. 执行机制 🔄

# 2.1 初始化顺序

Go 程序的初始化顺序如下:

  1. 包级别变量初始化
  2. init() 函数执行
  3. main() 函数执行
var X = 100  // 1. 首先初始化包级变量
func init() { // 2. 然后执行 init 函数
    X = X * 2
    fmt.Println("init:", X)
}
func main() { // 3. 最后执行 main 函数
    fmt.Println("main:", X)
}
// 输出:
// init: 200
// main: 200

# 2.2 多包依赖时的执行顺序

当涉及多个包时,初始化顺序遵循以下规则:

  1. 按照包的依赖关系,从最底层依赖包开始初始化
  2. 同一个包中的多个 init() 函数按照它们在文件中的顺序执行
// package A
package a
func init() { fmt.Println("init A") }
// package B
package b
import "a"
func init() { fmt.Println("init B") }
// package main
package main
import "b"
func init() { fmt.Println("init main") }
// 输出顺序:
// init A
// init B
// init main

# 3. init () 函数的特性 📋

# 3.1 基本特性

  1. 无参数无返回值
func init() {
    // 正确
}
func init(x int) {
    // 错误:不能有参数
}
func init() int {
    // 错误:不能有返回值
    return 0
}
  1. 自动执行:无需且不能显式调用
  2. 每个文件可以有多个 init ()
  3. 所有 init () 都会执行:即使包未被使用

# 3.2 使用场景

  1. 初始化复杂数据结构
var priorityMap map[string]int
func init() {
    priorityMap = make(map[string]int)
    priorityMap["high"] = 3
    priorityMap["medium"] = 2
    priorityMap["low"] = 1
}
  1. 注册模式
var drivers = make(map[string]Driver)
func init() {
    // 注册数据库驱动
    drivers["mysql"] = &MySQLDriver{}
    drivers["postgres"] = &PostgresDriver{}
}
  1. 环境检查
func init() {
    if os.Getenv("API_KEY") == "" {
        log.Fatal("API_KEY environment variable is required")
    }
}

# 4. 最佳实践 🌟

# 4.1 推荐做法

  1. 保持简单
func init() {
    // 简单的初始化
    cache.Init()
    metrics.Register()
}
  1. 错误处理
func init() {
    if err := loadConfig(); err != nil {
        log.Fatalf("Failed to load config: %v", err)
    }
}
  1. 初始化常量映射
var statusText map[int]string
func init() {
    statusText = map[int]string{
        200: "OK",
        400: "Bad Request",
        500: "Internal Server Error",
    }
}

# 4.2 避免的做法

  1. 避免复杂逻辑
// 不推荐
func init() {
    // 复杂的业务逻辑
    for _, user := range users {
        if user.IsAdmin {
            // 处理管理员逻辑
        }
    }
}
  1. 避免依赖顺序
// 不推荐
var db *sql.DB
func init() {
    // 初始化依赖其他 init 函数的结果
    if config == nil { // 依赖其他 init 中的 config 初始化
        log.Fatal("config not initialized")
    }
}
  1. 避免副作用
// 不推荐
func init() {
    // 修改全局状态
    os.Setenv("APP_MODE", "production")
}

# 5. 性能考虑 ⚡️

  1. 初始化开销
  • init () 函数在程序启动时执行
  • 过多的 init () 会影响启动时间
  1. 内存使用
  • 在 init () 中分配的内存会持续整个程序生命周期
  • 需要谨慎处理大数据结构的初始化
// 推荐:惰性初始化
var expensive *BigStruct
var once sync.Once
func getExpensive() *BigStruct {
    once.Do(func() {
        expensive = &BigStruct{}
    })
    return expensive
}

# 6. 调试技巧 🔍

  1. 跟踪 init 执行
GODEBUG=inittrace=1 go run main.go
  1. 查看初始化依赖
go build -v -x

记住:init () 函数是强大的工具,但应该谨慎使用。保持简单、清晰的初始化逻辑,避免复杂的依赖关系。