Go语言 init函数详解

你好,我今天想跟你聊聊 Go语言里面一个很有趣、很重要的小机制 —— init函数。 init虽然平时用得不多,但每次用到,它都是救场王。 我尽量用很自然的方式告诉你,到底init是干什么的,它的规则又是什么。


什么是 init 函数?

简单说,init函数就是Go语言帮我在程序启动时自动调用的函数。 我不需要去手动调用它,也不用担心忘了调用。只要我写了,它就一定会在合适的时候自己跑起来。

它的主要目的是:

  • 在程序真正运行之前,做一些必要的初始化工作

  • 比如,设置一些全局变量,连接数据库,或者提前加载一些资源。

总结一句话

init是专门为初始化准备的小工人,默默在程序启动时帮我打好地基。


init函数的基本特点

我这里总结了一些init函数的“性格”,你得了解清楚:

  • 没有参数,也没有返回值。 写的时候不能传参,也不能有返回值。

  • 每个包可以有多个init函数。 可以在一个文件里写多个init,也可以分散在不同文件里,各自独立。

  • 执行顺序是确定的,不会乱。

    • 先初始化依赖的包,再初始化当前包。

    • 一个包内,按照文件的编译顺序(一般是文件名字母顺序)执行init

    • 同一个文件内,多个init按照写的顺序执行。

  • main包里的init最后执行。 所有引入的包init跑完之后,才轮到main里的init


举个简单例子

我写段小代码,演示一下。

a.go
package main
​
import "fmt"
​
func init() {
    fmt.Println("init from a.go")
}
​
func main() {
    fmt.Println("main function")
}

运行一下,输出是:

init from a.go
main function

你看,initmain先执行了。


再举一个多包的例子

比如我有两个包,pkg1main

pkg1/pkg1.go
package pkg1
​
import "fmt"
​
func init() {
    fmt.Println("pkg1 init")
}
​
func Pkg1Func() {
    fmt.Println("pkg1 function")
}
main.go
package main
​
import (
    "fmt"
    "yourproject/pkg1"
)
​
func init() {
    fmt.Println("main init")
}
​
func main() {
    fmt.Println("main function")
    pkg1.Pkg1Func()
}

运行结果:

pkg1 init
main init
main function
pkg1 function

注意到没有?

  • pkg1init先跑,

  • 然后才是main自己的init

  • 最后才是main()函数。

因为Go的规则是:先初始化被依赖的包,然后才到自己。


一个包里有多个init怎么办?

这其实也很好理解。 如果我在同一个文件里写了两个init,Go会按照我写的顺序来执行。 如果是不同文件,就按照文件名字母排序,一个个来。

例如:

a.go
package main
​
import "fmt"
​
func init() {
    fmt.Println("init in a.go")
}
b.go
package main
​
import "fmt"
​
func init() {
    fmt.Println("init in b.go")
}

运行结果是:

init in a.go
init in b.go
main function

因为a.go字母比b.go小,先编译a.go,自然也先执行a.go的init


为什么需要 init?我自己main里初始化不行吗?

这个问题我一开始也想过。 实际上,init就是为了解耦初始化和执行逻辑

比如说:

  • 某个工具库,里面有个变量需要在程序一开始就设置好。

  • 但又不希望使用者去记得显式调用某个Setup()函数。

  • 这时候用init,包引入了,初始化也就自动做好了,使用体验更好。

再比如数据库连接:

var DB *sql.DB
​
func init() {
    var err error
    DB, err = sql.Open("mysql", "user:password@/dbname")
    if err != nil {
        log.Fatal(err)
    }
}

只要引入了这个包,数据库就连接好了,其他人用DB就行了,不需要每次都记得去初始化。


注意事项 ⚡

这里我提醒一下几点小坑,别踩了:

  • init尽量轻量! 不要做特别重的操作,比如网络IO、循环特别多的逻辑,容易拖慢启动速度。

  • 不要写成依赖顺序复杂的init链。 比如A的init里要等B,B又等A,那就完了,死循环或者出错了。

  • 保持可读性。 如果初始化逻辑很复杂,不如拆出去单独写成Setup()方法,让init里只是简单调用。


总结

我自己理解init,其实就像程序的幕后化妆师。 观众(用户)只看到程序光鲜亮丽地跑起来了, 而init在台下悄悄布置好灯光、化好妆、摆好舞台。

它不抢戏,但没有它,戏根本开不了场。


小结版

特性说明
自动调用程序启动时自动执行
无参数无返回值语法上必须这样
顺序执行按导入顺序、文件顺序
轻量快速建议避免耗时操作
适合做初始化工作变量初始化、连接配置

👉 立即点击链接,开启你的全栈开发之路:Golang全栈开发完整课程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值