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

 更新时间:2021年05月06日 10:49:55   作者:zhuxinquan61  
这篇文章主要介绍了浅谈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语言中函数new与make的使用和区别

    深入讲解Go语言中函数new与make的使用和区别

    大家都知道Go语言中的函数new与函数make一直是新手比较容易混淆的东西,看着相似,但其实不同,不过解释两者之间的不同也非常容易,下面这篇文章主要给大家介绍了关于Go语言中函数new与make区别的相关资料,需要的朋友可以参考下。
    2017-10-10
  • GO 语言运行环境的基础知识

    GO 语言运行环境的基础知识

    这篇文章主要介绍了GO 语言运行环境的基础知识的相关资料,需要的朋友可以参考下
    2022-09-09
  • Go语言中log日志库的介绍

    Go语言中log日志库的介绍

    本文给大家介绍Go语言中log日志库的概念使用技巧,log包定义了Logger类型,该类型提供了一些格式化输出的方法,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-10-10
  • go语言题解LeetCode506相对名次示例详解

    go语言题解LeetCode506相对名次示例详解

    这篇文章主要为大家介绍了go语言题解LeetCode506相对名次示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 解决golang处理http response碰到的问题和需要注意的点

    解决golang处理http response碰到的问题和需要注意的点

    这篇文章主要介绍了解决golang处理http response碰到的问题和需要注意的点,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • GoLang实现日志收集器流程讲解

    GoLang实现日志收集器流程讲解

    这篇文章主要介绍了GoLang实现日志收集器流程,看日志是开发者平时排查BUG所必须的掌握的技能,但是日志冗杂,所以写个小工具来收集这些日志帮助我们排查BUG,感兴趣想要详细了解可以参考下文
    2023-05-05
  • Golang 官方依赖注入工具wire示例详解

    Golang 官方依赖注入工具wire示例详解

    这篇文章主要为大家介绍了Golang 官方依赖注入工具wire示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • golang 进度条功能实现示例

    golang 进度条功能实现示例

    这篇文章主要介绍了golang 进度条功能实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • Go语言学习之指针的用法详解

    Go语言学习之指针的用法详解

    这篇文章主要为大家详细介绍了Go语言中指针的用法,文中的示例代码讲解详细,对我们学习Go语言有一定的帮助,需要的可以参考一下
    2022-04-04
  • Go语言操作Excel利器之excelize类库详解

    Go语言操作Excel利器之excelize类库详解

    Excelize是Go语言编写的用于操作Office Excel文档基础库,基于ECMA-376,ISO/IEC 29500国际标准,可以使用它来读取、写入由Excel 2007及以上版本创建的电子表格文档,下面这篇文章主要给大家介绍了关于Go语言操作Excel利器之excelize类库的相关资料,需要的朋友可以参考下
    2022-10-10

最新评论