Hi,你們好,我是明哥。git
在本身學習 Golang 的這段時間裏,我寫了詳細的學習筆記放在個人我的微信公衆號 《Go編程時光》,對於 Go 語言,我也算是個初學者,所以寫的東西應該會比較適合剛接觸的同窗,若是你也是剛學習 Go 語言,不防關注一下,一塊兒學習,一塊兒成長。github
個人在線博客:golang.iswbm.com 個人 Github:github.com/iswbm/GolangCodingTimegolang
在 Go 語言中,一個包可包含多個 .go
文件(這些文件必須得在同一級文件夾中),只要這些 .go
文件的頭部都使用 package
關鍵字聲明瞭同一個包。編程
導入包主要可分爲兩種方式:數組
import "fmt"
import "sync" 複製代碼
import(
"fmt"
"sync"
)複製代碼
如你所見,Go 語言中 導入的包,必須得用雙引號包含,在這裏吐槽一下。微信
在一些場景下,咱們可能須要對導入的包進行從新命名,好比app
import (
"crypto/rand"
mrand "math/rand" // 將名稱替換爲mrand避免衝突
)複製代碼
import hw "helloworldtestmodule"複製代碼
import pathpkg "path"複製代碼
如裏在咱們程序內部裏頻繁使用了一個工具包,好比 fmt,那每次使用它的打印函數打印時,都要 包名+方法名。函數
對於這種使用高頻的包,能夠在導入的時,就把它定義會 "本身人
"(方法是使用一個 .
),本身人的話,不分彼此,它的方法,就是咱們的方法。工具
今後,咱們打印不再用加 fmt 了。學習
import . "fmt"
func main() {
Println("hello, world")
}複製代碼
但這種用法,會有必定的隱患,就是導入的包裏可能有函數,會和咱們本身的函數發生衝突。
每一個包都容許有一個 init
函數,當這個包被導入時,會執行該包的這個 init
函數,作一些初始化任務。
對於 init
函數的執行有兩點須要注意
init
函數優先於 main
函數執行
在一個包引用鏈中,包的初始化是深度優先的。好比,有這樣一個包引用關係:main→A→B→C,那麼初始化順序爲
C.init→B.init→A.init→main複製代碼
當咱們導入一個包時,若是這個包沒有被使用到,在編譯時,是會報錯的。
可是有些狀況下,咱們導入一個包,只想執行包裏的 init
函數,來運行一些初始化任務,此時怎麼辦呢?
可使用匿名導入,用法以下,其中下劃線爲空白標識符,並不能被訪問
// 註冊一個PNG decoder
import _ "image/png"複製代碼
因爲導入時,會執行 init 函數,因此編譯時,仍然會將這個包編譯到可執行文件中。
當咱們使用 import 導入 testmodule/foo
時,初學者,常常會問,這個 foo
究竟是一個包呢,仍是隻是包所在目錄名?
import "testmodule/foo"複製代碼
爲了得出這個結論,專門作了個試驗(請看「第七點裏的代碼示例」),最後得出的結論是:
據我瞭解在 Go 1.10 以前,好像是不支持相對導入的,在 Go 1.10 以後才能夠。
絕對導入:從 $GOPATH/src
或 $GOROOT
或者 $GOPATH/pkg/mod
目錄下搜索包並導入
相對導入:從當前目錄中搜索包並開始導入。就像下面這樣
import (
"./module1"
"../module2"
"../../module3"
"../module4/module5"
)複製代碼
分別舉個例子吧
1、使用絕對導入
有以下這樣的目錄結構(注意確保當前目錄在 GOPATH 下)
其中 main.go 是這樣的
package main
import (
"app/utilset" // 這種使用的就是絕對路徑導入
)
func main() {
utils.PrintHello()
}複製代碼
而在 main.go 的同級目錄下,還有另一個文件夾 utilset
,爲了讓你理解 「第六點:import 導入的是路徑而不是包」,我在 utilset 目錄下定義了一個 hello.go
文件,這個go文件定義所屬包爲 utils
。
package utils
import "fmt"
func PrintHello(){
fmt.Println("Hello, 我在 utilset 目錄下的 utils 包裏")
}複製代碼
運行結果以下
2、使用相對導入
仍是上面的代碼,將絕對導入改成相對導入後
將 GOPATH 路徑設置回去(請對比上面使用絕對路徑的 GOPATH)
而後再次運行
總結一下,使用相對導入,有兩點須要注意
項目不要放在 $GOPATH/src
下,不然會報錯(好比我修改當前項目目錄爲GOPATH後,運行就會報錯)
Go Modules 不支持相對導入,在你開啓 GO111MODULE 後,沒法使用相對導入。
最後,不得不說的是:使用相對導入的方式,項目可讀性會大打折扣,不利用開發者理清整個引用關係。
因此通常更推薦使用絕對引用的方式。使用絕對引用的話,又要談及優先級了
前面一節,介紹了三種不一樣的包依賴管理方案,不一樣的管理模式,存放包的路徑可能都不同,有的能夠將包放在 GOPATH 下,有的能夠將包放在 vendor 下,還有些包是內置包放在 GOROOT 下。
那麼問題就來了,若是在這三個不一樣的路徑下,有一個相同包名可是版本不一樣的包,咱們導入的時候,是選擇哪一個進行導入呢?
這就須要咱們搞懂,在 Golang 中包搜索路徑優先級是怎樣的?
這時候就須要區分,是使用哪一種模式進行包的管理的。
若是使用 govendor
當咱們導入一個包時,它會:
vendor
目錄中查找$GOROOT/src
目錄下查找$GOPATH/src
目錄下查找爲了驗證這個過程,我在建立中建立一個 vendor 目錄後,就開啓了 vendor 模式了,我在 main.go 中隨便導入一個包 pkg,因爲這個包是我隨便指定的,固然會找不到,找不到就會報錯, Golang 會在報錯信息中打印中搜索的過程,從這個信息中,就能夠看到 Golang 的包查找優先級了。
若是使用 go modules
你導入的包若是有域名,都會先在 $GOPATH/pkg/mod
下查找,找不到就連網去該網站上尋找,找不到或者找到的不是一個包,則報錯。
而若是你導入的包沒有域名(好比 "fmt"這種),就只會到 $GOROOT
裏查找。
還有一點很重要,當你的項目下有 vendor 目錄時,無論你的包有沒有域名,都只會在 vendor 目錄中想找。
一般vendor
目錄是經過 go mod vendor
命令生成的,這個命令會將項目依賴所有打包到你的項目目錄下的 verdor 文件夾中。
系列導讀
24. 超詳細解讀 Go Modules 前世此生及入門使用