go 语言中 init() 函数是什么时候执行的?

一、init() 函数什么时候执行?

  在Go语言中,init 函数是一个特殊的函数,它在包被导入时自动执行,主要用于完成程序的初始化工作。例如初始化包级别的变量,或者执行包初始化时仅需执行一次的设置,如初始化资源、初始化数据库连接、载入本地配置文件、预先计算一些值、注册等。

  在 golang 中, init() 函数是在main() 函数之前执行的。

package main

func init() {
	println("init function called")
}

func main() {
	println("main function called")
}

运行结果:

在这里插入图片描述

二、init() 函数特点

  • init() 函数是可选的,源文件可以没有 init() 函数。
  • init() 函数没有入参和返回值,自动执行,不能被其他函数调用。(与 main 函数一样)
  • 同一个包,甚至是同一个源文件可以有多个 init() 函数。
  • 不管包被导入多少次,包内的 init 函数只会执行一次

三、代码执行顺序

  Go语言代码执行顺序为:import –> const –> var –>init()–>main()

1、初始化所有被导入的包;
2、初始化被导入的包所有全局常量、变量;
3、执行被导入的包 init() 函数;
4、层层递出最后执行 main() 函数。

在这里插入图片描述

需要注意的点:

  • 程序初始化的顺序是,从被导入的最深层包开始进行初始化,层层递出最后到 main 包;
  • Go的依赖分析器会尽可能并行地初始化包;
  • 程序的初始化和执行都起始于 main 包。如果 main 包还导入了其它的包,那么就会在编译时将它们依次导入;
  • 有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到 fmt 包,但它只会被导入一次,因为没有必要导入多次);
  • 不同包的init函数按照包导入的依赖关系决定执行顺序。
  • 在编码时避免依赖 init() 函数的执行顺序。虽然 init() 顺序是明确的,但代码可以更改,init() 函数之间的关系可能会使代码变得脆弱和容易出错,因此在编码时避免依赖 init() 函数的执行顺序。
// a 包
// a.go
package a

import _ "myGoProject/init_demo/b"

var a = func() int {
	println("init int a")
	return 'a'
}()

func init() {
	println("a init function called")
}


// b 包
// b.go
package b

import _ "myGoProject/init_demo/c"

var b = func() int {
	println("init int b")
	return 'b'
}()

func init() {
	println("b init function called")
}


// c 包
// c.go
package c

var c = func() int {
	println("init int c")
	return 'c'
}()

func init() {
	println("c init function called")
}


// main 包
// main.go
package main

import (
	_ "myGoProject/init_demo/a"
)

var m = func() int {
	println("init int m")
	return 1
}()

func init() {
	println("init function called")
}

func main() {
	println("main function called")
}

运行结果:

在这里插入图片描述

  结论:包级变量是在 init 函数之前完成初始化的。

四、多个 init() 函数执行顺序

  通过上面的内容我们可以知道,一个源文件可以有多个 init 函数,一个包也可以有多个 init 函数。 那么不同包、不同源文件中的多个 init 函数是按照什么顺序执行的呢?下面,我们通过一个个的小 demo 去分析。

1、一个源文件中多个 init() 函数

  main.go 源文件有三个 init() 函数。

package main

func init() {
	println("a init function called")
}

func init() {
	println("b init function called")
}

func init() {
	println("c init function called")
}

func main() {
	println("main function called")
}

运行结果:

在这里插入图片描述

  结论:一个源文件中多个 init() 函数的执行顺序是根据定义顺序确定,从上到下

  验证:把 3个 init() 函数的定义顺序改为 b、a、c。运行结果如下,执行顺序为 b、a、c。结论正确。

package main

func init() {
	println("b init function called")
}

func init() {
	println("a init function called")
}

func init() {
	println("c init function called")
}

func main() {
	println("main function called")
}

在这里插入图片描述

2、一个包中多个 init() 函数

  main 包有4个源文件 a.go、b.go、c.go、main.go。

在这里插入图片描述

// a.go
package main

func init() {
	println("a init function called")
}

// b.go
package main

func init() {
	println("b init function called")
}

// c.go
package main

func init() {
	println("c init function called")
}

// main.go
package main

func init() {
	println("init function called")
}

func main() {
	println("main function called")
}

运行结果:

在这里插入图片描述
  结论:一个包中多个 init() 函数的执行顺序是根据文件名的字典序确定,从小到大

  验证:把 a.go 文件名改为 d.go,内容不变。运行结果如下,先执行 b.go、c.go 的 init() 函数,然后才执行 d.go 的init() 函数。结论正确。

在这里插入图片描述
在这里插入图片描述

3、多个包中多个 init() 函数(不存在依赖)

  有a、b、c、main 4个包,每个包有1个同名源文件 a.go、b.go、c.go、main.go。

在这里插入图片描述

// a 包
// a.go
package a

func init() {
	println("a init function called")
}

// b 包
// b.go
package b

func init() {
	println("b init function called")
}

// c 包
// c.go
package c

func init() {
	println("c init function called")
}

// main 包
// main.go
package main

import (
	_ "myGoProject/init_demo/a"
	_ "myGoProject/init_demo/b"
	_ "myGoProject/init_demo/c"
)

func init() {
	println("init function called")
}

func main() {
	println("main function called")
}

运行结果:

在这里插入图片描述
  结论:多个包中多个 init() 函数,包不相互依赖,多个 init() 函数的执行顺序是根据 main 包中导入顺序确定,从上到下,最后再执行 main 包的 init 函数。

  验证:把导入顺序改为b、c、a,其他代码不变。运行结果如下,先执行 b、c 包的 init() 函数,然后才执行 a 包的init() 函数,最后执行 main 包的 init 函数。结论正确。

import (
	_ "myGoProject/init_demo/b"
	_ "myGoProject/init_demo/c"
	_ "myGoProject/init_demo/a"
)

在这里插入图片描述

4、多个包中多个 init() 函数(存在依赖)

  有a、b、c、main 4 个包,每个包有1个同名源文件 a.go、b.go、c.go、main.go,main 包依赖 a 包,a 包依赖 b 包,b 包依赖 c 包,依赖关系为 main > a > b > c。

在这里插入图片描述

// a 包
// a.go
package a

import _ "myGoProject/init_demo/b"

func init() {
	println("a init function called")
}

// b 包
// b.go
package b

import _ "myGoProject/init_demo/c"

func init() {
	println("b init function called")
}

// c 包
// c.go
package c

func init() {
	println("c init function called")
}

// main 包
// main.go
package main

import (
	_ "myGoProject/init_demo/a"
)

func init() {
	println("init function called")
}

func main() {
	println("main function called")
}

运行结果:

在这里插入图片描述

  结论:多个包中多个 init() 函数,包存在依赖,多个 init() 函数的执行顺序是根据包导入的依赖关系确定,从里到外,执行顺序为最后被依赖的包(最深层的包:c 包)最先被初始化,如依赖关系 main > a > b > c,则 init 函数执行顺序为 c > b > a > main。

  验证:把依赖关系改为 main > b > a > c,其他代码不变。运行结果如下,c 包的 init() 函数先执行,接着执行 a 包的init() 函数,然后执行 b 包的 init 函数,最后执行 main 包的 init 函数。结论正确。

// a 包
// a.go
package a

import _ "myGoProject/init_demo/c"

func init() {
	println("a init function called")
}

// b 包
// b.go
package b

import _ "myGoProject/init_demo/a"

func init() {
	println("b init function called")
}

// c 包
// c.go
package c

func init() {
	println("c init function called")
}

// main 包
// main.go
package main

import (
	_ "myGoProject/init_demo/b"
)

func init() {
	println("init function called")
}

func main() {
	println("main function called")
}

在这里插入图片描述

5、一个包会被多个包同时导入,它只会被导入一次,init() 函数只执行一次

  main 包依赖 a、c 包,a 包依赖 b、c 包,b 包依赖 c 包,c 包被 main、a、b 包依赖。

// a 包
// a.go
package a

import (
	_ "myGoProject/init_demo/b"
	_ "myGoProject/init_demo/c"
)

func init() {
	println("a init function called")
}

// b 包
// b.go
package b

import _ "myGoProject/init_demo/c"

func init() {
	println("b init function called")
}

// c 包
// c.go
package c

func init() {
	println("c init function called")
}

// main 包
// main.go
package main

import (
	_ "myGoProject/init_demo/a"
	_ "myGoProject/init_demo/c"
)

func init() {
	println("init function called")
}

func main() {
	println("main function called")
}


运行结果:

在这里插入图片描述

五、小结

  在 golang 中, init() 函数是在main() 函数之前执行的。全局常量、变量是在 init 函数之前初始化的。Go语言代码执行顺序为:import –> const –> var –>init()–>main()。

多个init() 函数执行顺序:
  1、一个源文件中多个 init() 函数的执行顺序是根据定义顺序确定,从上到下
  2、 一个包中多个 init() 函数的执行顺序是根据文件名的字典序确定,从小到大
  3、多个包中多个 init() 函数,包不相互依赖,多个 init() 函数的执行顺序是根据 main 包中导入顺序确定,从上到下,最后再执行 main 包的 init 函数。
  4、多个包中多个 init() 函数,包存在依赖,多个 init() 函数的执行顺序是根据包导入的依赖关系确定,从里到外,执行顺序为最后被依赖的包(最深层的包:c 包)最先被初始化,如依赖关系 main > a > b > c,则 init 函数执行顺序为 c > b > a > main。
  5、一个包会被多个包同时导入,它只会被导入一次,init() 函数只执行一次。

  在编码时避免依赖 init() 函数的执行顺序。虽然 init() 顺序是明确的,但代码可以更改,init() 函数之间的关系可能会使代码变得脆弱和容易出错,因此在编码时避免依赖 init() 函数的执行顺序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值