欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一篇文章讀懂Golang?init函數執(zhí)行順序

 更新時間:2022年11月01日 08:56:20   作者:愛碼大鯉魚  
init()函數會在包被初始化后自動執(zhí)行,并且在main()函數之前執(zhí)行,但是需要注意的是init()以及main()函數都是無法被顯式調用的,下面這篇文章主要給大家介紹了關于如何通過一篇文章讀懂Golang?init函數執(zhí)行順序的相關資料,需要的朋友可以參考下

1.init 函數簡介

Golang init 函數是一種特殊的函數,主要用于完成程序的初始化工作,如初始化數據庫的連接、載入本地配置文件、根據命令行參數初始化全局變量等。

package main

import "flag"

var gopath string

func init() {
	println("init a")
}

func init() {
	println("init b")
}

func init() {
	println("init c")
	// gopath may be overridden by --gopath flag on command line.
	flag.StringVar(&gopath, "gopath", "/root/go", "override default GOPATH")
}

func main() {
	println("main")
	flag.Parse()
	println(gopath)
}

運行輸出:

$ go run main.go --gopath="/home/alice/go"
init a
init b
init c
main
/home/alice/go

之所以特殊,是因為 init 函數有如下特點:

  • init 函數是可選的,可以沒有;
  • 與 main 函數一樣,不能有入參與返回值;
  • 與 main 函數一樣,init 會自動執(zhí)行,不能被其他函數調用;
  • 一個包內可以有多個 init 函數,即可以在包的多個源文件中定義多個 init 函數。一般建議在與包同名源文件中寫一個 init 函數,這樣可讀性好且便于維護;
  • 一個源文件可以有多個 init 函數。

2.執(zhí)行順序

既然一個程序可以有多個 init 函數,那么對于位于不同包、不同源文件中的多個 init 函數,其執(zhí)行順序是怎樣的呢?

下面從多個方面去考察。

2.1 單個源文件的 init 執(zhí)行順序

package main

func init() {
	println("init a")
}

func init() {
	println("init b")
}

func init() {
	println("init c")
}

func main() {
	println("main")
}

運行輸出:

$ go run main.go
init a
init b
init c
main

結論: 同一個源文件的 init 函數執(zhí)行順序與其定義順序一致,從上到下。

2.2 單個包的 init 執(zhí)行順序

假設 main 包有三個源文件 a.go,b.go 和 c.go。

// a.go
package main

func init() {
	println("init a")
}

// b.go
package main

func init() {
	println("init b")
}

// c.go
package main

func init() {
	println("init c")
}

// main.go
package main

func init() {
	println("init main")
}

func main() {
	println("main")
}

編譯運行輸出:

$ go build && ./main
init a
init b
init c
init main
main

結論: 同一個包中不同源文件 init 函數的執(zhí)行順序,是根據文件名的字典序來確定。

2.3 main 包導入多個包時 init 執(zhí)行順序

2.3.1 不存在依賴

對于不同的包,如果不相互依賴的話,在 main 包中被 import,那么這種情況下,各個包的 init 執(zhí)行順序是怎樣的呢?

假設有包 a,b 和 c,在 main.go 中被 import。

// a 包
// a.go
package a

func init() {
	println("init a")
}

// b 包
// b.go
package b

func init() {
	println("init b")
}

// c 包
// c.go
package c

func init() {
	println("init c")
}

// main 包
// main.go
package main

import (
	_ "main/a"
	_ "main/b"
	_ "main/c"
)

func init() {
	println("init main")
}

func main() {
	println("main")
}

編譯運行輸出:

$ go build && ./main
init a
init b
init c
init main
main

結論: 對于不同的包,如果不相互依賴的話,按照 main 包中導入順序調用包的 init 函數,最后再調用 main 包的 init 函數。

2.3.2 存在依賴

對于不同的包,如果存在依賴關系的話,在 main 包中被 import,那么這種情況下,各個包的 init 執(zhí)行順序是怎樣的呢?

假設有包 a,b 和 c,main 包 import a 包,a import b,b import c,即依賴關系為 main > a > b > c。

// a 包
// a.go
package a

import _ "main/b"

func init() {
	println("init a")
}

// b 包
// b.go
package b

import _ "main/c"

func init() {
	println("init b")
}

// c 包
// c.go
package c

func init() {
	println("init c")
}

// main 包
// main.go
package main

import (
	_ "main/a"
)

func init() {
	println("init main")
}

func main() {
	println("main")
}

編譯運行輸出:

$ go build && ./main
init c
init b
init a
init main
main

結論: 如果 package 存在依賴,不同包的 init 函數按照包導入的依賴關系決定執(zhí)行順序。 調用順序為最后被依賴的最先被初始化,如導入順序 main > a > b > c,則初始化順序為 c > b > a > main,依次執(zhí)行對應的 init 方法。

2.4 包級變量初始化與 init 函數執(zhí)行順序

如果包中存在包級變量,那么其初始化與 init 函數執(zhí)行先后順序如何呢?

還是假設有包 a,b 和 c,main 包 import a 包,a import b,b import c,即依賴關系為 main > a > b > c。且每個包都有一個包級變量,并通過函數完成其初始化。

// a 包
// a.go
package a

import _ "main/b"

var A = func() string {
	println("init var A")
	return "A"
}()

func init() {
	println("init a")
}

// b 包
// b.go
package b

import _ "main/c"

var B = func() string {
	println("init var B")
	return "B"
}()

func init() {
	println("init b")
}

// c 包
// c.go
package c

var C = func() string {
	println("init var C")
	return "C"
}()

func init() {
	println("init c")
}

// main 包
// main.go
package main

import (
	_ "main/a"
)

var m = func() string {
	println("init var m")
	return "m"
}()

func init() {
	println("init main")
}

func main() {
	println("main")
}

編譯運行輸出:

$ go build && ./main
init var C
init c
init var B
init b
init var A
init a
init var m
init main
main

結論: 可見每個包的包級變量初始化是在 init 函數執(zhí)行之前完成的。不同包的 init 函數與包級變量的初始化順序如下圖所示。

3.小結

Golang 中的 init 是一種特殊的函數,主要用于完成程序的初始化工作。其特點有:

  • init 函數是可選的,可以沒有;
  • 與 main 函數一樣,不能有入參與返回值;
  • 與 main 函數一樣,init 會自動執(zhí)行,不能被其他函數調用;
  • 一個包內可以有多個 init 函數,即可以在包的多個源文件中定義多個 init 函數。一般建議在與包同名源文件中寫一個 init 函數,這樣可讀性好且便于維護;
  • 一個源文件可以有多個 init 函數。

程序中如果在不同包的不同源文件有多個 init 函數時,其執(zhí)行順序可概述為:

  • 同一個源文件的 init 函數執(zhí)行順序與其定義順序一致,從上到下;
  • 同一個包中不同文件的 init 函數的執(zhí)行順序按照文件名的字典序;
  • 對于不同的包,如果不相互依賴的話,按照 main 包中 import 的順序調用其包中的 init 函數;
  • 如果包存在依賴,不同包的 init 函數按照包導入的依賴關系決定執(zhí)行順序。 調用順序為最后被依賴的最先被初始化,如導入順序 main > a > b > c,則初始化順序為 c > b > a > main,依次執(zhí)行對應的 init 方法;
  • 如果包存在包級變量,則先于包的 init 函數完成初始化。

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

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

請務必銘記于心,雖然 init() 順序是明確的,但代碼可以更改,init() 函數之間的關系可能會使代碼變得脆弱和容易出錯,因此在編碼時避免依賴 init() 函數的執(zhí)行順序。

參考文獻

總結 

到此這篇關于一篇文章讀懂Golang init函數執(zhí)行順序的文章就介紹到這了,更多相關Golang init函數執(zhí)行順序內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • go Antlr重構腳本解釋器實現(xiàn)示例

    go Antlr重構腳本解釋器實現(xiàn)示例

    這篇文章主要為大家介紹了go Antlr重構腳本解釋器實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • 一篇文章學會GO語言中的變量

    一篇文章學會GO語言中的變量

    Go語言是靜態(tài)類型語言,因此變量有明確類型的,編譯器也會檢查變量類型的正確性,下面這篇文章主要給大家介紹了關于GO語言中變量的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-07-07
  • 詳解Go語言中for range的

    詳解Go語言中for range的"坑"

    這篇文章主要介紹了詳解Go語言中for range的"坑",文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-09-09
  • go基礎語法50問及方法詳解

    go基礎語法50問及方法詳解

    這篇文章主要為大家介紹了go基礎語法50問及方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • golang文件內容覆蓋問題的分析及解決

    golang文件內容覆蓋問題的分析及解決

    通過golang讀取數據庫站點映射配置,生成nginx conf文件,并檢查和重啟nginx服務,已達到站點自動化部署目的,當目標文件中內容很長,而寫入的內容很短時,目標文件內容無法完全覆蓋,本文給大家介紹了解決方法,需要的朋友可以參考下
    2024-01-01
  • golang防止內存逃逸的方法小結

    golang防止內存逃逸的方法小結

    在Go語言中,內存逃逸是指在函數中分配的變量在函數結束后仍然被引用,防止內存逃逸有助于提高程序的性能,因為棧上分配的內存可以更快地被回收,本文給大家總結了一些防止內存逃逸的方法,需要的朋友可以參考下
    2024-02-02
  • Golang控制通道實現(xiàn)協(xié)程等待詳解

    Golang控制通道實現(xiàn)協(xié)程等待詳解

    這篇文章主要介紹了Golang控制通道實現(xiàn)協(xié)程等待,通道是Go語言程序的并發(fā)體goroutine是它們之間的通信機制。一個通道是一個通信機制,它可以讓一個goroutine通過它給另一個goroutine發(fā)送值信息。每個通道都有一個特殊的類型,也就是channels可發(fā)送數據的類型
    2022-11-11
  • Golang中runtime的使用詳解

    Golang中runtime的使用詳解

    這篇文章主要介紹了Golang中runtime的使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-08-08
  • golang gorm中格式化時間問題詳解

    golang gorm中格式化時間問題詳解

    這篇文章主要給大家介紹了關于golang gorm中格式化時間問題的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用golang具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-07-07
  • 通過Golang編寫一個AES加密解密工具

    通過Golang編寫一個AES加密解密工具

    這篇文章主要為大家詳細介紹了如何利用Golang制作一個AES加密解密工具,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2022-05-05

最新評論