一、前言
之前有一次我在写程序的时候,创建了一个init函数func init()
,起初我并不知道init()
是go本身自带的一个函数,类似于main()
,于是我给init()
函数设置返回值时,Goland提示错误,当时很纳闷,百度了才知道这回事。
由此,写一篇博客,以记录init()
函数用法。
二、init函数介绍
init()
函数是一个特殊的函数,存在以下特性:
- 不能被其他函数调用,而是在main函数执行之前,自动被调用
- init函数不能作为参数传入
- 不能有传入参数和返回值
以下是《the way to go》中的解释:
变量除了可以在全局声明中初始化,也可以在
init ()
函数中初始化。这是一类非常特殊的函数,它不能够被人为调用,而是在每个包完成初始化后自动执行,并且执行优先级比 main 函数高。每个源文件都只能包含一个
init()
函数(此处存在错误)。初始化总是以单线程执行,并且按照包的依赖关系顺序执行。一个可能的用途是在开始执行程序之前对数据进行检验或修复,以保证程序状态的正确性。
1、每个源文件能包含多个init函数:
package main
import "fmt"
//经测试,每个源文件可以包含多个init函数,而且会按先后顺序执行,优先级高于main
func main() {
fmt.Println("main")
}
func init() {
fmt.Println("init1")
}
func init() {
fmt.Println("init2")
}
上述程序的运行结果是:
init1
init2
main
2、导包顺序决定init函数执行顺序:
go中不同包中init函数的执行顺序是根据包的导入关系决定的。
//=====================main包
package main
import (
"fmt"
_ "mytest/level1"
)
func main() {
fmt.Println("main")
}
//=====================level1包
package level1
import (
"fmt"
_ "mytest/level2"
)
func init() {
fmt.Println("level1 init")
}
//=====================level2包
package level2
import "fmt"
func init() {
fmt.Println("level2 init")
}
从以上代码可以看出,main
包导入level1
包,而在level1
包内导入level2
包。
执行结果:
level2 init
level1 init
main
下图(图源自网络)可以总结这种顺序关系
三、init函数的应用场景举例
1、对变量进行初始化
package trans
import "math"
var Pi float64
func init() {
Pi = 4 * math.Atan(1)
}
2、当一个程序开始之前调用后台执行的 goroutine
func init() {
go backend()
}
3、用于注册驱动
//=====================postgres包
package postgres
import (
"database/sql"
"database/sql/driver"
"errors"
)
// PostgresDriver provides our implementation for the
// sql package.
type PostgresDriver struct{}
// Open provides a connection to the database.
func (dr PostgresDriver) Open(string) (driver.Conn, error) {
return nil, errors.New("Unimplemented")
}
var d *PostgresDriver
// init is called prior to main.
func init() {
d = new(PostgresDriver)
sql.Register("postgres", d)
}
//=====================main包
package main
import (
"database/sql"
_ "github.com/goinaction/code/chapter3/dbdriver/postgres"
)
func main() {
sql.Open("postgres", "mydb")
}