likes
comments
collection
share

golang init 函数执行顺序本文通过编写demo的方式,解释说明了golang中init函数的执行顺序以及常量、

作者站长头像
站长
· 阅读数 73

序言

本文通过编写demo,解释说明golang init函数的执行顺序以及特点。

1. init的作用以及执行特点

Golang 中的 init 函数是每个 Go 文件默认存在的,而且可以有多个,顾名思意,init 函数的作用就是为程序初始化提供一个入口。在不同的包和 Go 文件中,init 函数的执行顺序是有一定规则的。最近在工作中遇到了一些与初始化相关的问题,于是这里编写了一个 demo 来验证 init 函数的执行顺序。想快速了解的同学,可以直接查看最后的图解,它清晰地展示了 init 函数的执行流程和优先级。

1.1 同一个go文件中可以有零个或多个init函数,其中他们的执行顺序与init代码的前后顺序有关

1.1.1 测试案例1——测试同一个go文件多个init的执行顺序

package main

import (
    "fmt"
)

func init() {
    fmt.Println(" init func 1 starting")
}

func init() {
    fmt.Println(" init func 2 starting")
}

func init() {
    fmt.Println(" init func 3 starting")
}

func main() {
    fmt.Println(" main func starting")
}

从执行结果可以得出init方法的执行顺序是按照代码位置顺序来执行的 golang init 函数执行顺序本文通过编写demo的方式,解释说明了golang中init函数的执行顺序以及常量、 这时候我们把第三个init的代码顺序移动到2前面去

package main

import (
    "fmt"
)

func init() {
    fmt.Println(" init func 1 starting")
}

func init() {
    fmt.Println(" init func 3 starting")
}

func init() {
    fmt.Println(" init func 2 starting")
}

func main() {
    fmt.Println(" main func starting")
}

可以看见执行结果

golang init 函数执行顺序本文通过编写demo的方式,解释说明了golang中init函数的执行顺序以及常量、 由此可以证明1.1的结论

1.2 同一个包里,init的执行是以包为单位的,只要某个包的函数在其他包被使用,那么这个包内所有go文件的所有init函数都执行,顺序就是go文件排列顺序,并且与该使用的函数在哪个go文件无关。

1.2.1 测试案例2

目录结构

golang init 函数执行顺序本文通过编写demo的方式,解释说明了golang中init函数的执行顺序以及常量、 测试代码

// main包的main.go文件
package main

import (
    "fmt"
    "go_test/testInit/testinitOne"
)

func main() {
    testinitOne.NewTestInit()
    fmt.Println(" main func starting")
}
// testinitOne包的1.go文件
package testinitOne

import "fmt"

func init() {
    fmt.Println(" 这是 testInitOne 包的1.go文件的init执行")
}
// testinitOne包的1a.go文件,2.go和a.go以及b.go输出同理
package testinitOne

import "fmt"

func init() {
    fmt.Println(" 这是 testInitOne 包的1a.go文件的init执行")
}

执行main函数后输出结果,可以看见执行结果与文件顺序相同,并且没有因为NewTestInit()函数在b.go就执行b.go文件的init,所以说,init的执行是以包为单位的,而不是看被外部使用的方法在哪个go文件就先执行哪个go文件的init。

golang init 函数执行顺序本文通过编写demo的方式,解释说明了golang中init函数的执行顺序以及常量、

1.3 有关包级常量和变量执行顺序:常量的初始化->包级的变量的初始化->init

1.3.1 测试案例3

目录结构 golang init 函数执行顺序本文通过编写demo的方式,解释说明了golang中init函数的执行顺序以及常量、 代码

// main.go 代码
package main

import (
    "fmt"
    "go_test/testInit/testConstAndVar"
)

func main() {
    testConstAndVar.UserPackageTestConstAndVar()
    fmt.Println("main starting")
}
// testInit代码
package testConstAndVar

import "fmt"

const PackageName = "const testConstAndVar init finish"

var VarName = func() string {
    fmt.Println(PackageName) // 成功打印出来了说明常量以及初始化完了,之后才初始化包级变量
    fmt.Println("now is init var : testConstAndVar.vatName")
    return "varName"
}()

func UserPackageTestConstAndVar() {
    fmt.Println("UserPackageTestConstAndVar() starting")
}

执行结果

golang init 函数执行顺序本文通过编写demo的方式,解释说明了golang中init函数的执行顺序以及常量、

2. 大全套案例,贯通初始化流程

目录结构

golang init 函数执行顺序本文通过编写demo的方式,解释说明了golang中init函数的执行顺序以及常量、 代码:

/**********testInitA包*****************/
// 1.go
package testInitA

import "fmt"

func init() {
    fmt.Println("testInitA 1.go 第一个init函数 init")
}

func init() {
    fmt.Println("testInitA 1.go 第二个init函数 init")
}

//const A1 = A2 + 1
//const A2 = A1 + 1

const testInitString1GO = "testInitA包的1.go文件的常量初始化完毕"

var testInitA1GOVar = func() string {

    fmt.Println(testInitString1GO)
    fmt.Println("testInitA包的1.go文件的包级变量testInitA1GOVar初始化")
    return "testInitA包的1.go文件的变量"
}()
// 1A.go
package testInitA

import "fmt"

func init() {
    fmt.Println("testInitA 1A.go init")
}
// 1B.go
package testInitA

import "fmt"

func init() {
    fmt.Println("testInitA 1B.go init")
}
// 1C.go
package testInitA

import "fmt"

func init() {
    fmt.Println("testInitA 1C.go init")
}

const testInitString1CGO = "testInitA包的1C.go文件的常量初始化完毕"

var testInitA1CGOVar = func() string {

    fmt.Println(testInitString1CGO)
    fmt.Println("testInitA包的1C.go文件的包级变量testInitA1CGOVar初始化")
    return "testInitA包的1C.go文件的变量"
}()

func NewPackageTestInitA() {
}
/*******testInitB包*******/
// 1.go
package testInitB

import "fmt"

func init() {
    fmt.Println("testInitB 1.go init")
}
//1A.go
package testInitB

import "fmt"

const testInitString1AGO = "testInitB包的1A.go文件的常量初始化完毕"

var testInitB1AGOVar = func() string {

    fmt.Println(testInitString1AGO)
    fmt.Println("testInitB包的1A.go文件的包级变量testInitB1AGOVar初始化")
    return "testInitB包的1A.go文件的变量"
}()

func init() {
    fmt.Println("testInitB 1B.go init")
}

func NewPackageTestInitB() {
}
/********testInit包******/
// main.go
package main

import (
    "fmt"
    "go_test/testInit/testInitA"
    "go_test/testInit/testInitB"
)

const testMainConstInit = "testInit包的main.go文件的常量初始化完毕"

var testMainConstInitVar = func() string {

    fmt.Println(testMainConstInit)
    fmt.Println("testInit包的main.go文件的包级变量testMainConstInitVar初始化")
    return "testInit包的的main.go文件的变量"
}()

func init() {
    fmt.Println("main.go init1")
}

func init() {
    fmt.Println("main.go init2")
}

func main() {
    testInitA.NewPackageTestInitA()
    testInitB.NewPackageTestInitB()
    fmt.Println("main.go starting")
}

执行结果

golang init 函数执行顺序本文通过编写demo的方式,解释说明了golang中init函数的执行顺序以及常量、 可以看见这里的执行顺序是按照const->var->init来执行的,这里需要注意到是如下两行const是一起初始化的,而不是按照如下打印的顺序,因为没想到什么更好的测试办法,所以对于const的初始化结果是采取直接打印来判断的,这里读者需要清除,同一个包下的所有const需要都初始化完毕,才会进行到var初始化,而不是按照如下图的打印顺序,除此之外图中其他的打印顺序即是初始化的顺序。

golang init 函数执行顺序本文通过编写demo的方式,解释说明了golang中init函数的执行顺序以及常量、 这里对于const初始化不难想到一个要点,const初始化如果之间有相互依赖,类似于包的依赖,也是先初始化不依赖其他const的const,同理const也有可能初出现像包一样的循环依赖的问题,如下图就会出现循环依赖的问题,编译不会通过,不光const、package,var也是一样的,所有发生循环依赖的地方编译都不会通过。

const A1 = A2 + 1
const A2 = A1 + 1

golang init 函数执行顺序本文通过编写demo的方式,解释说明了golang中init函数的执行顺序以及常量、

3.总结:话不多说,一图胜千言

golang init 函数执行顺序本文通过编写demo的方式,解释说明了golang中init函数的执行顺序以及常量、

转载自:https://juejin.cn/post/7425822400179240997
评论
请登录