来源: mp.weixin.qq.com/s/HptVvXho3…golang
欢迎关注公众号《Go后端干货》面试
各类Go,后端技术,面试题分享后端
咱们知道Go程序的入口是main函数,当main函数退出了,程序也就退出了。init函数在Go程序中也扮演着重要的角色。这篇文章将会介绍init函数的特性以及如何使用它们。bash
init函数的做用:函数
若是须要使用一个导入的包,首先要对这个包进行初始化,这一步在main函数执行以前,由runtime来完成,分如下步骤:ui
若是某个包被导入了屡次,也只会执行一次包的初始化。spa
Go一个包中能够包含不少文件,那么变量的初始化顺序与各个包的init函数执行顺序又是怎样的呢?code
首先,runtime的初始化依赖机制会启动,当初始化依赖机制计算完成后,就须要决定a.go和z.go中的变量谁先初始化,这取决于呈现给编译器的文件顺序,通常来讲是按文件名的字典序,可是变量间或各个包间有依赖须要另外讨论。若是z.go先被传到build系统,那么z.go的变量初始化就比a.go先一步完成。orm
同一个包中,变量的初始化顺序是按文件名的字典序,但同时runtime也会解析变量间依赖关系,没有依赖的变量最早初始化,init函数的执行顺序也同理。cdn
来看下面按文件名字典序初始化的例子:
package main
import "fmt"
var _ int64 = s()
func init() {
fmt.Println("init in sandbox.go")
}
func s() int64 {
fmt.Println("calling s() in sandbox.go")
return 1
}
func main() {
fmt.Println("main")
}
复制代码
package main
import "fmt"
var _ int64 = a()
func init() {
fmt.Println("init in a.go")
}
func a() int64 {
fmt.Println("calling a() in a.go")
return 2
}
复制代码
package main
import "fmt"
var _ int64 = z()
func init() {
fmt.Println("init in z.go")
}
func z() int64 {
fmt.Println("calling z() in z.go")
return 3
}
复制代码
calling a() in a.go
calling s() in sandbox.go
calling z() in z.go
init in a.go
init in sandbox.go
init in z.go
main
复制代码
下面是按依赖关系决定初始化顺序的例子。
package pack
import (
"fmt"
"test_util" // 引入test_util包
)
var Pack int = 6
func init() {
a := test_util.Util
fmt.Println("init pack ", a)
}
复制代码
package test_util
import "fmt"
var Util int = 5
func init() {
fmt.Println("init test_util")
}
复制代码
package main
import (
"fmt"
"pack"
"test_util"
)
func main() {
fmt.Println(pack.Pack)
fmt.Println(test_util.Util)
}
复制代码
init test_util
init pack 5
6
5
复制代码
因为pack包的初始化依赖test_util,所以运行时会先初始化test_util包再初始化pack包;
init函数不须要传入参数也没有返回值,并且init函数是不能被其余函数调用的。
package main
import "fmt"
func init() {
fmt.Println("init")
}
func main() {
init()
}
复制代码
上面的代码会报编译错误:undefined: init。
在一个文件中也能够有多个init函数,看下面代码。
package main
import "fmt"
func init() {
fmt.Println("init 1")
}
func init() {
fmt.Println("init 2")
}
func main() {
fmt.Println("main")
}
复制代码
package main
import "fmt"
func init() {
fmt.Println("init 3")
}
复制代码
输出:
init 1
init 2
init 3
main
复制代码
init函数的也普遍用在标准库中,好比math
,bzip2
,image
。
最经常使用的是初始化不能使用初始化表达式的变量,也就是不能在变量声明的时候初始化的变量,看如下例子。
var square [10]int
func init() {
for i := 0; i < 10; i++ {
square[i] = i * i
}
}
复制代码
咱们常常会在开源代码中见到有些导入的包中前面加了个下划线”_“,这表示只是想执行包中的init函数。
import _ "image/png"
复制代码
image/png
包里的init函数做用是向image
包注册png图片的解码器,见src/image/png/reader.go
func init() {
image.RegisterFormat("png", pngHeader, Decode, DecodeConfig)
}
复制代码
当心而且不要滥用init函数,由于对于复杂点的项目来讲,init函数的执行顺序难以捉摸。
1.《init functions in Go》 medium.com/golangspec/…
2.《五分钟理解golang的init函数》zhuanlan.zhihu.com/p/34211611
3.《When is the init() function run?》stackoverflow.com/questions/2…