Go 的 init 函数在包首次被引用时按依赖拓扑序自动执行,不接受参数、无返回值且不可显式调用;同一包内多 init 按文件名字典序执行,变量初始化先于 init,但依赖包的 init 必须已完成。

Go 的 init 函数在包加载时自动执行
Go 程序启动前,运行时会按依赖顺序初始化所有导入的包,每个包里的 init 函数都会被调用一次,且仅在包首次被引用时触发。它不接受参数、无返回值,也不能被显式调用——哪怕你写 init(),编译器也会报错 undefined: init。
多个 init 函数的执行顺序是确定的
同一个包内可以定义多个 init 函数(比如分散在不同 .go 文件里),它们按源文件名的字典序依次执行;而跨包的顺序严格遵循“依赖图拓扑排序”:被依赖的包先于依赖它的包初始化。例如:main 包 import http,http import net,那么执行顺序一定是 net.init → http.init → main.init。
- 同一文件中多个
init函数?不允许,编译报错multipleinitfunctions - 不同文件但同名(如 a.go 和 b.go)?允许,按文件名排序,
a.go里的init先于b.go - 循环 import?编译直接失败,不会进入初始化阶段
init 和变量初始化的先后关系容易混淆
包级变量的初始化表达式(包括常量、var 声明的右值)在 init 函数之前求值,但注意:如果变量初始化依赖了其他包的导出变量,则那个包必须已初始化完毕(即其 init 已执行)。例如:
package foo var X = bar.Y // bar.Y 必须已在 bar.init 中完成赋值
常见陷阱是误以为 init 是“第一个执行的”,其实包级变量的零值或字面量初始化更早;只有含函数调用、全局对象构造等逻辑才应放进 init。
立即学习“go语言免费学习笔记(深入)”;
调试 init 执行顺序可以用 go build -gcflags="-m" 或打日志
当初始化行为异常(比如空指针 panic、配置未加载),优先检查是否因依赖顺序导致某个 init 访问了尚未初始化的变量。简单验证方式是在各 init 里加 fmt.Println("in package xxx init"),配合 go run main.go 观察输出顺序。注意:测试文件(_test.go)中的 init 仅在运行 go test 时触发,且和主程序分离。
真正复杂的是带副作用的 init(比如注册 handler、修改全局 flag、启动 goroutine),这类代码一旦跨包耦合,就很难预测行为——别把它当成“启动脚本”来用,能延迟到 main 开始后再做的,就别塞进 init。










