同一文件内多个init函数按源码自上而下顺序执行,不可调用、无参数、无返回值,且早于包级变量初始化和main函数执行。

init 函数不是“按需初始化入口”,而是包加载时的自动钩子;它早于 main、不可调用、不接受参数、不返回值——想靠它读配置或等 flag 解析,大概率失败。
多个 init 函数在同一个文件里怎么执行
同一源文件中允许定义多个 init 函数,它们按代码出现顺序从上到下执行。编译器不会报错,但顺序完全由书写位置决定,和函数名、注释无关。
- 不能写成
init()调用,否则报错:undefined: init - 如果两个
init都操作同一个全局变量,后声明的那个会覆盖前一个的效果 - 调试时建议加日志,例如
log.Println("sub.go init #1"),避免依赖 stdout 且被缓冲
不同文件的 init 执行顺序由文件名决定
Go 编译器按文件名的字典序(ASCII)遍历并执行每个文件里的所有 init 函数。比如 a.go 的 init 一定在 b.go 之前,z_config.go 会排到最后。
- 不要靠改文件名来“控制初始化时机”,这会让团队协作和重构变得脆弱
- 若需明确依赖关系(如 A 初始化完才能初始化 B),应把逻辑合并进一个
init,或改用显式Init()函数 + 手动调用 -
go tool compile -S main.go可看到生成的初始化调用链,但日常开发几乎用不到
跨包导入时 init 的触发顺序
被导入包的 init 总是先于导入方执行。例如 import "net/http" 会先触发 net 包的 init,再触发 http 包的 init,最后才执行当前包的 init。
立即学习“go语言免费学习笔记(深入)”;
- 即使某个包没被
main直接 import,只要被某条依赖链间接引用,它的init就会跑 - 如果包完全未被引用(包括未被测试文件 import),它的
init根本不会触发 - 循环 import 会导致编译失败,
init顺序在这种情况下无意义
init 里能安全做什么
只做无副作用、不依赖运行时输入的操作:注册驱动(database/sql)、预设日志前缀、检查环境变量是否存在、初始化常量映射表。
- 别在
init里调flag.Parse()或读os.Args—— 它们还没准备好 - 别启动 goroutine 后不等待,
main返回后程序退出,goroutine 被强制终止 - 别在
init中panic,错误堆栈只显示runtime.main,难定位 - 需要外部输入的初始化(如配置路径、数据库地址),应挪到
main开头或封装为config.Init()
最常被忽略的一点:包级变量带函数调用的初始化表达式(如 var x = loadConfig())会在任何 init 函数之前执行。你以为的“初始化顺序”,其实早已被变量声明悄悄决定了。


















