佳木斯湛栽影视文化发展公司

主頁 > 知識庫 > 淺談golang package中init方法的多處定義及運行順序問題

淺談golang package中init方法的多處定義及運行順序問題

熱門標(biāo)簽:呼叫中心市場需求 服務(wù)外包 鐵路電話系統(tǒng) AI電銷 Linux服務(wù)器 百度競價排名 網(wǎng)站排名優(yōu)化 地方門戶網(wǎng)站

在不了解這個問題之前,在網(wǎng)上搜索一下竟然搜出了兩個完全相反的結(jié)果,所以打算自己測試下這個問題。

首先給出結(jié)論:

在同一個package中,可以多個文件中定義init方法

在同一個go文件中,可以重復(fù)定義init方法

在同一個package中,不同文件中的init方法的執(zhí)行按照文件名先后執(zhí)行各個文件中的init方法

在同一個文件中的多個init方法,按照在代碼中編寫的順序依次執(zhí)行不同的init方法

下面看下測試的代碼:

在當(dāng)前目錄下新建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中導(dǎo)入testinit package,然后go build main.go,執(zhí)行顯示如下:

從運行的結(jié)構(gòu)就能很清晰的看到,123、ini1、ini2三個文件按照文件名執(zhí)行,對于ini1.go中的兩個ini方法按照init方法編寫的先后順序執(zhí)行,最后才執(zhí)行的main方法!

補充:Golang中defer、return、返回值和main、init函數(shù)的陷阱

Go語言中延遲函數(shù)defer充當(dāng)著 cry...catch 的重任,使用起來也非常簡便,然而在實際應(yīng)用中,很多gopher并沒有真正搞明白defer、return和返回值之間的執(zhí)行順序。他們的特點:

多個defer的執(zhí)行順序為“后進先出”;

defer、return、返回值三者的執(zhí)行邏輯應(yīng)該是:return最先執(zhí)行,return負責(zé)將結(jié)果寫入返回值中;接著defer開始執(zhí)行一些收尾工作;最后函數(shù)攜帶當(dāng)前返回值退出。

如何解釋兩種結(jié)果的不同:

上面兩段代碼的返回結(jié)果之所以不同,其實從上面第2條結(jié)論很好理解。

a()int 函數(shù)的返回值沒有被提前聲名,其值來自于其他變量的賦值,而defer中修改的也是其他變量,而非返回值本身,因此函數(shù)退出時返回值并沒有被改變。

b()(i int) 函數(shù)的返回值被提前聲名,也就意味著defer中是可以調(diào)用到真實返回值的,因此defer在return賦值返回值 i 之后,再一次地修改了 i 的值,最終函數(shù)退出后的返回值才會是defer修改過的值。

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

雖然 c()*int 的返回值沒有被提前聲明,但是由于 c()*int 的返回值是指針變量,那么在return將變量 i 的地址賦給返回值后,defer再次修改了 i 在內(nèi)存中的實際值,因此函數(shù)退出時返回值雖然依舊是原來的指針地址,但是其指向的內(nèi)存實際值已經(jīng)被成功修改了。

Go里面有兩個保留的函數(shù):init函數(shù)(能夠應(yīng)用于所有的package)和main函數(shù)(只能應(yīng)用于package main)。這兩個函數(shù)在定義時不能有任何的參數(shù)和返回值。雖然一個package里面可以寫任意多個init函數(shù),但這無論是對于可讀性還是以后的可維護性來說,我們都強烈建議用戶在一個package中每個文件只寫一個init函數(shù)。

Go程序會自動調(diào)用init()和main(),所以你不需要在任何地方調(diào)用這兩個函數(shù)。每個package中的init函數(shù)都是可選的,但package main就必須包含一個main函數(shù)。

程序的初始化和執(zhí)行都起始于main包。如果main包還導(dǎo)入了其它的包,那么就會在編譯時將它們依次導(dǎo)入。有時一個包會被多個包同時導(dǎo)入,那么它只會被導(dǎo)入一次(例如很多包可能都會用到fmt包,但它只會被導(dǎo)入一次,因為沒有必要導(dǎo)入多次)。

當(dāng)一個包被導(dǎo)入時,如果該包還導(dǎo)入了其它的包,那么會先將其它包導(dǎo)入進來,然后再對這些包中的包級常量和變量進行初始化,接著執(zhí)行init函數(shù)(如果有的話),依次類推。

等所有被導(dǎo)入的包都加載完畢了,就會開始對main包中的包級常量和變量進行初始化,然后執(zhí)行main包中的init函數(shù)(如果存在的話),最后執(zhí)行main函數(shù)。

下圖詳細地解釋了整個執(zhí)行過程:

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。

您可能感興趣的文章:
  • 為什么不建議在go項目中使用init()
  • go語言的初始化順序,包,變量,init詳解
  • Go語言init函數(shù)詳解
  • GO語言ini配置文件的讀取的操作

標(biāo)簽:崇左 湘潭 湖南 仙桃 蘭州 銅川 衡水 黃山

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《淺談golang package中init方法的多處定義及運行順序問題》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266
    右玉县| 汕头市| 岱山县| 荆门市| 昌乐县| 河北省| 乌鲁木齐市| 玛多县| 确山县| 舒兰市| 大姚县| 聂拉木县| 高碑店市| 全州县| 五大连池市| 闻喜县| 台前县| 隆回县| 那曲县| 曲靖市| 安多县| 吴忠市| 大竹县| 延川县| 建瓯市| 崇仁县| 汪清县| 肥乡县| 绵竹市| 蓝田县| 贵定县| 麟游县| 遂川县| 元阳县| 阜阳市| 庆城县| 宜城市| 宣威市| 威宁| 鸡东县| 高邮市|