Go語言中的包
我們在使用其他語言,比如Java,Python,都有類似包的概念,Go也不例外,其核心思想即為分組和模組化。人的大腦對龐大和複雜的事情很難掌控,可以對其採用分而治之的策略,使其模組化,從而更容易管理。 如下是標準庫中net包的樹形結構圖
net
├─http
│ ├─cgi
│ │ └─testdata
│ ├─cookiejar
│ ├─fcgi
│ ├─httptest
│ ├─httptrace
│ ├─httputil
│ ├─internal
│ ├─pprof
│ └─testdata
├─internal
│ └─socktest
├─mail
├─rpc
│ └─jsonrpc
├─smtp
├─testdata
├─textproto
└─url 庫包和main包
當把一個程式的package聲明為main的時候(並且該檔案包含一個main函數),則表示最終該程式要編譯為一個可執行檔程式。如果程式未聲明為main,則可以編譯為一個庫的形式。 包的匯入
通過import匯入包之後,即可使用包中的變數和函數,如下源碼,我們定義了一個lib包,裡面定義了加,減,乘,除4個方法,然後在另一個包中來使用。
庫中的內容如下:
package lib1func Add(x, y int) int { z := x + y return z}func Sub(x, y int) int { z := x - y return z}func Mul(x, y int) int { z := x * y return z}func Div(x, y int) int { z := x / y return z}
main包的內容如下:
package mainimport ( "fmt" "lib1")func main() { x := 8 y := 2 a := lib1.Add(x, y) b := lib1.Sub(x, y) c := lib1.Mul(x, y) d := lib1.Div(x, y) fmt.Println(a) fmt.Println(b) fmt.Println(c) fmt.Println(d)}
輸出:
10
6
16
4
注意庫包中匯出的方法若想讓包外可見,則第一個字母要大寫。如Add,Sub… 否則該方法對包外不可見,僅可在本包中使用(變數也有同樣的原則)。Go語言沒有提供類似C++和Java的private,public,protected等關鍵字,也反應了Go語言的哲學思想:簡單實用至上。 包名匯入衝突(命名匯入)
在使用import匯入包的時候,我們就可以通過包名稱引用包中的方法,如上面的lib1,但是如果有又有另一個人開發的包名稱也為lib1(這裡只包的最後一部分),也就是發生的包命名衝突該如何處理呢。可以在import的名稱前面起一個別名處理此問題,如下分別把“fmt”和“lib1”的包名稱重新命名為了“fmta”和“liba”
package mainimport ( fmta "fmt" liba "lib1")func main() { x := 8 y := 2 a := liba.Add(x, y) b := liba.Sub(x, y) c := liba.Mul(x, y) d := liba.Div(x, y) fmta.Println(a) fmta.Println(b) fmta.Println(c) fmta.Println(d)}
包的初始化函數 init()
一個包中可能有多個檔案,如果我們想對這個包做一些初始化操作該如何做呢。Go提供了一個既沒有參數也沒有傳回值的init()函數(類似main函數), 可以在init函數中進行初始化操作。 比如有如下3個包“lib1”,“lib2”“m1”分別代表兩個庫包和一個包含main函數的主包,每個包中有對應的Go檔案,包目錄結構如下:
├─lib1 a.go
├─lib2 b.go
└─m1 m.go
“m1”包中的m.go檔案會import“lib1”,進一步“lib1”會import“lib2”。且在lib1和lib2中定義了init函數。源碼如下:
//lib2中的a2.gopackage lib2import ( "fmt")func init() { fmt.Println("lib2 init1")}func init() { fmt.Println("lib2 init2")}
// lib1中的a1.gopackage lib1import ( "fmt" _ "lib2")func init() { fmt.Println("lib1 init1")}func init() { fmt.Println("lib1 init2")}
//m1中的m.gopackage mainimport ( "fmt" _ "lib1")func main() { fmt.Println("main")}
輸出:
lib2 init1
lib2 init2
lib1 init1
lib1 init2
main
可見main最後執行,首先執行的是import各個包時候的init。而且init函數執行的順序是依據依賴順序來執行的。Go會遞迴對依賴進行分析,然後從main包開始分析,首先保證沒有循環相依性。main包發現import了“lib1”,lib1發現又import了“lib2”,因此首先執行lib2的init函數,再返回執行lib1中的init函數,最後返回到main函數來執行。
每個包中可以有多個檔案,每個檔案中也可以有多個init()函數,但是這些init函數的順序Go是沒有保證的。 包的匿名匯入
因為Go對匯入但是未使用的包,會報編譯錯誤。因此可以使用如下的方式。在包名稱之前使用一個底線”_”來表示。這樣仍然會執行所import包中的init函數。
package lib1import ( "fmt" _ "lib2")