• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    浅谈golang package中init方法的多处定义及运行顺序问题

    在不了解这个问题之前,在网上搜索一下竟然搜出了两个完全相反的结果,所以打算自己测试下这个问题。

    首先给出结论:

    在同一个package中,可以多个文件中定义init方法

    在同一个go文件中,可以重复定义init方法

    在同一个package中,不同文件中的init方法的执行按照文件名先后执行各个文件中的init方法

    在同一个文件中的多个init方法,按照在代码中编写的顺序依次执行不同的init方法

    下面看下测试的代码:

    在当前目录下新建main.go及testinit目录,在testinit目录下共有三个文件:123.go、ini1.go、ini2.go,各个源码文件分别如下:

    123.go

    package testinit
    import "fmt"
    func init(){
        fmt.Println("123init")
    }
    

    ini1.go

    package testinit
    import "fmt"
    func init(){
        fmt.Println("init1")
    }
    func init(){
        fmt.Println("init1-2")
    }
    

    ini2.go

    package testinit
    import "fmt"
    func init(){
        fmt.Println("init2")
    }
    

    main.go

    package main
    import (
        _ "./testinit"
        "fmt"
    )
    func main(){
        fmt.Println("main")
    }
    

    如上main.go中导入testinit package,然后go build main.go,执行显示如下:

    从运行的结构就能很清晰的看到,123、ini1、ini2三个文件按照文件名执行,对于ini1.go中的两个ini方法按照init方法编写的先后顺序执行,最后才执行的main方法!

    补充:Golang中defer、return、返回值和main、init函数的陷阱

    Go语言中延迟函数defer充当着 cry...catch 的重任,使用起来也非常简便,然而在实际应用中,很多gopher并没有真正搞明白defer、return和返回值之间的执行顺序。他们的特点:

    多个defer的执行顺序为“后进先出”;

    defer、return、返回值三者的执行逻辑应该是:return最先执行,return负责将结果写入返回值中;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出。

    如何解释两种结果的不同:

    上面两段代码的返回结果之所以不同,其实从上面第2条结论很好理解。

    a()int 函数的返回值没有被提前声名,其值来自于其他变量的赋值,而defer中修改的也是其他变量,而非返回值本身,因此函数退出时返回值并没有被改变。

    b()(i int) 函数的返回值被提前声名,也就意味着defer中是可以调用到真实返回值的,因此defer在return赋值返回值 i 之后,再一次地修改了 i 的值,最终函数退出后的返回值才会是defer修改过的值。

    package main 
    import (
     "fmt"
    )
     
    func main() { 
     fmt.Println("c return:", *(c())) // 打印结果为 c return: 2
     
    }
     
    func c() *int {
     var i int
     defer func() {
      i++
      fmt.Println("c defer2:", i) // 打印结果为 c defer: 2
     }()
     
     defer func() {
      i++
      fmt.Println("c defer1:", i) // 打印结果为 c defer: 1
     }()
     return i
    }

    虽然 c()*int 的返回值没有被提前声明,但是由于 c()*int 的返回值是指针变量,那么在return将变量 i 的地址赋给返回值后,defer再次修改了 i 在内存中的实际值,因此函数退出时返回值虽然依旧是原来的指针地址,但是其指向的内存实际值已经被成功修改了。

    Go里面有两个保留的函数:init函数(能够应用于所有的package)和main函数(只能应用于package main)。这两个函数在定义时不能有任何的参数和返回值。虽然一个package里面可以写任意多个init函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个package中每个文件只写一个init函数。

    Go程序会自动调用init()和main(),所以你不需要在任何地方调用这两个函数。每个package中的init函数都是可选的,但package main就必须包含一个main函数。

    程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。

    当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。

    等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。

    下图详细地解释了整个执行过程:

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。如有错误或未考虑完全的地方,望不吝赐教。

    您可能感兴趣的文章:
    • 为什么不建议在go项目中使用init()
    • go语言的初始化顺序,包,变量,init详解
    • Go语言init函数详解
    • GO语言ini配置文件的读取的操作
    上一篇:go语言使用Casbin实现角色的权限控制
    下一篇:解决Goland 同一个package中函数互相调用的问题
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    浅谈golang package中init方法的多处定义及运行顺序问题 浅谈,golang,package,中,init,