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


Posted in Golang onMay 06, 2021

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

首先给出结论:

在同一个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,执行显示如下:

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

从运行的结构就能很清晰的看到,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函数。

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

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

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

Golang 相关文章推荐
go语言求任意类型切片的长度操作
Apr 26 Golang
Go语言带缓冲的通道实现
Apr 26 Golang
go语言中切片与内存复制 memcpy 的实现操作
Apr 27 Golang
解决Golang中ResponseWriter的一个坑
Apr 27 Golang
彻底理解golang中什么是nil
Apr 29 Golang
go语言中fallthrough的用法说明
May 06 Golang
go mod 安装依赖 unkown revision问题的解决方案
May 06 Golang
go 实现简易端口扫描的示例
May 22 Golang
go goroutine 怎样进行错误处理
Jul 16 Golang
一文搞懂Golang 时间和日期相关函数
Dec 06 Golang
GO语言异常处理分析 err接口及defer延迟
Apr 14 Golang
Golang 结构体数据集合
Apr 22 Golang
golang switch语句的灵活写法介绍
May 06 #Golang
go语言中fallthrough的用法说明
Golang之sync.Pool使用详解
May 06 #Golang
Golang 编译成DLL文件的操作
May 06 #Golang
完美解决golang go get私有仓库的问题
May 05 #Golang
golang gopm get -g -v 无法获取第三方库的解决方案
May 05 #Golang
go类型转换及与C的类型转换方式
May 05 #Golang
You might like
PHP+DBM的同学录程序(3)
2006/10/09 PHP
linux下为php添加curl扩展的方法
2011/07/29 PHP
PHP数据对象映射模式实例分析
2019/03/29 PHP
JQUERY 实现窗口滚动搜索框停靠效果(类似滚动停靠)
2013/03/27 Javascript
『JavaScript』限制Input只能输入数字实现思路及代码
2013/04/22 Javascript
javaScript面向对象继承方法经典实现
2013/08/20 Javascript
Js获取下拉框选定项的值和文本的实现代码
2014/02/26 Javascript
Ionic快速安装教程
2016/06/03 Javascript
JavaScript实现时间倒计时跳转(推荐)
2016/06/28 Javascript
bootstrap fileinput 上传插件的基础使用
2017/02/17 Javascript
微信小程序日期时间选择器使用方法
2018/02/01 Javascript
el-select 下拉框多选实现全选的实现
2019/08/02 Javascript
JavaScript实现身份证验证代码实例
2019/08/26 Javascript
JavaScript canvas实现雨滴特效
2021/01/10 Javascript
Python数据分析之双色球统计两个红和蓝球哪组合比例高的方法
2018/02/03 Python
Python各类图像库的图片读写方式总结(推荐)
2018/02/23 Python
浅谈Python2、Python3相对路径、绝对路径导入方法
2018/06/22 Python
python做接口测试的必要性
2019/11/20 Python
Python Pillow.Image 图像保存和参数选择方式
2020/01/09 Python
Python中包的用法及安装
2020/02/11 Python
Python 之 Json序列化嵌套类方式
2020/02/27 Python
numpy的Fancy Indexing和array比较详解
2020/06/11 Python
基于python requests selenium爬取excel vba过程解析
2020/08/12 Python
详解Python中的GIL(全局解释器锁)详解及解决GIL的几种方案
2021/01/29 Python
HTML 5 input placeholder 属性如何完美兼任ie
2014/05/12 HTML / CSS
h5移动端调用支付宝、微信支付的实现
2020/06/08 HTML / CSS
SHEIN香港:价格实惠的女性时尚服装
2018/08/14 全球购物
Ray-Ban雷朋西班牙官网:全球领先的太阳眼镜品牌
2018/11/28 全球购物
美国战术品牌:5.11 Tactical
2019/05/01 全球购物
this关键字的作用
2016/01/30 面试题
预备党员对照检查材料思想汇报
2014/09/24 职场文书
幼儿园三八妇女节活动总结
2015/02/06 职场文书
销售内勤岗位职责范本
2015/04/13 职场文书
新娘婚礼致辞
2015/07/27 职场文书
MySQL数据库优化之通过索引解决SQL性能问题
2022/04/10 MySQL
win10滚动条自动往上跑怎么办?win10滚动条自动往上跑的解决方法
2022/08/05 数码科技