包(package)是多個Golang源碼的集合,是一種高級的代碼復(fù)用方案,換言之Golang的【源碼復(fù)用】建立在package包基礎(chǔ)之上。
聲明包
Go語言中任何源碼文件必須屬于某個包,同時源碼文件的第一行有效代碼必須是package packageName語句,通過此語句來聲明自己所在的包。
package packageName
Go語言的包借助了文件目錄樹的組織形式,一般包名就是源文件所在的目錄名。雖然Go語言沒有強制要求包名必須和目錄同名,但這樣做使結(jié)構(gòu)更加清晰。因此一個包可簡單理解為一個存放.go文件的文件夾。
包與文件夾
包是Go語言中代碼組成和代碼編譯的主要方式,Go語言中允許將同一個包的代碼分割成多個獨立的源碼文件來單獨保存,只需要將獨立文件存放在同一個目錄下即可。一個包中可以具有任意多個文件,文件名字無規(guī)定但后綴必須為.go。
- Go語言中的包與文件夾是一一對應(yīng)的,所有包相關(guān)的操作必須依賴于工作目錄
GOPATH。 - 包可以定義在很深的目錄結(jié)構(gòu)中,包名的定義是不會包括文件目錄的,但在包的引用時一般會使用全路徑引用。
- 文件夾下所有源碼只能屬于同一個包,屬于同一個包的源碼不能放在多個文件夾下。
包名
- 包名一般采用小寫,建議使用簡短且有意義的名稱。
- 包名一般要和所在的目錄同名,也可以不同,包名中不能含有
-等特殊符號。 - 包一般會使用域名作為目錄名稱,以保證包名的唯一性。
主包
包名為main的包是應(yīng)用程序的入口包,編譯時若沒有包含main包的源代碼則不會生成可執(zhí)行文件。
- 入口主函數(shù)
main()所在的包叫做main - 同一目錄下只能有且僅有一個
main主函數(shù) -
main包想要引用其它代碼,必須同樣以包的方式進行引用。
$ vim main.go
package main
func main(){
}
可見性
若要在某個包中引用另一個包中的標識符(如變量、常量、類型、函數(shù)等),該標識符必須是對外可見的(public)。Golang中僅需將標識符首字母大寫就可以讓標識符對外可見了。
導(dǎo)入包
要在代碼中引用其他包的時候,需要使用import關(guān)鍵字導(dǎo)入所需使用的包。
import "包的路徑"
-
import導(dǎo)入語句通常會放在源碼文件開頭包聲明語句的下面 - 導(dǎo)入的包名需要使用雙引號包裹起來
- 包名會從
GOPATH/src/后開始搜索,使用/進行路徑分割。 - 禁止循環(huán)導(dǎo)入包,包不能出現(xiàn)環(huán)形引用,多個包首尾相互引用時Go編譯器不能通過。
- 包的重復(fù)引用是允許的,Go編譯器保證包的初始化函數(shù)只會執(zhí)行一次。
包的導(dǎo)入有兩種寫法,分別是單行導(dǎo)入和多行導(dǎo)入。
- 單行導(dǎo)入
import "包的路徑"
import "包的路徑"
- 多行導(dǎo)入
import (
"包的路徑"
"包的路徑"
)
包的引用路徑有兩種寫法,分別是全路徑導(dǎo)入和相對路徑導(dǎo)入。
- 全路徑導(dǎo)入
包的絕對路徑是GOPATH/src或GOPATH/src后面包的存放路徑
例如:sql包是自定義的包,sql包的源碼位于GOPATH/src/database/sql目錄下。
import "database/sql"
- 相對路徑導(dǎo)入
相對路徑只能用于導(dǎo)入GOPATH工作目錄下的包,標準包的導(dǎo)入只能采用全路徑導(dǎo)入。
import "../packageName"
引用包
包的引用有4種格式
- 標準格式
import "fmt"
fmt.Println("hello world")
使用fmt.作為前綴來使用fmt包中的方法
- 自定義別名引用格式
導(dǎo)入包的時候可以為導(dǎo)入的包設(shè)置別名
import F "fmt"
F.Println("hello world")
F是fmt包的別名,使用時可以使用F.來代替標準引用格式的fmt.作為前綴來使用fmt包中的方法。
- 省略引用格式
import . "fmt"
Println("hello world")
省略引用格式相當于把fmt包直接合并到當前程序中,使用fmt包內(nèi)的方法時可以不添加fmt.前綴,直接引用。
- 匿名引用格式
當引用某個包時,若只希望執(zhí)行包初始化的init()函數(shù),而不使用包內(nèi)部的代碼可使用匿名引用格式。匿名導(dǎo)入的包與其他放到導(dǎo)入的包都會被編譯到可執(zhí)行文件中。
import _ "fmt"
使用標準格式引用包后若源碼中沒有使用則編譯器會報錯,若包中有init()初始化函數(shù),可通過import _ "包的路徑"這種方式來引用,
package main
import (
_ "database/sql"
. "fmt"
)
func Main(){
Println("hello world")
}
初始化函數(shù)
Golang程序執(zhí)行時導(dǎo)入包語句會自動觸發(fā)包內(nèi)部的init()函數(shù)的調(diào)用,init()函數(shù)沒有參數(shù)也沒有返回值,init()函數(shù)在程序運行時會自動被調(diào)用執(zhí)行,不能在代碼中主動調(diào)用它。
一個包可以存在多個init()初始化函數(shù),包加載時會執(zhí)行全部的初始化函數(shù),但并不能夠保證執(zhí)行順序,因此不建議在一個包中添加多個初始化函數(shù),建議將需要初始化的邏輯放到一個初始化函數(shù)即可。
如果僅僅執(zhí)行包的初始化函數(shù)init(),即使包沒有init()初始化函數(shù)也不會引發(fā)編譯器錯誤。
包初始化函數(shù)的執(zhí)行時機:全局聲明 > init() > main(),單個包的初始化過程會先初始化常量,然后是全局變量,最后才會執(zhí)行包的init()初始化函數(shù)。
加載包
Go程序啟動和加載是在執(zhí)行main包中的main()函數(shù)之前,Go引導(dǎo)程序會先對整個程序中的包進行初始化。
Golang會從main包開始檢查其導(dǎo)入的所有包,每個包中可能會導(dǎo)入其它包。包初始化程序會從main()函數(shù)所引用的包開始,逐級查找包的引用,直到找到?jīng)]有引用其它包的包,最終才會生成一個包引用的【有向無環(huán)圖】。Golang編譯器會將有向無環(huán)圖轉(zhuǎn)換為一棵樹,從樹的葉子節(jié)點開始逐層向上對包進行初始化。

Golang編譯器由此構(gòu)建出一個樹狀的包引用關(guān)系,根據(jù)引用順序決定編譯順序,依次編譯這些包中的代碼,運行時被最后導(dǎo)入的包會最先初始化并調(diào)用其init()函數(shù)

內(nèi)置包
標準Go語言代碼庫中包含了大量的包,Go安裝時會自動安裝到系統(tǒng),可在GOROOT/src/pkg目錄下查找這些包。
常見內(nèi)置包
| 內(nèi)置包 | 描述 |
|---|---|
| fmt | 格式化標準輸入輸出,類似C語言中的printf和scanf。 |
| io | 提供原始的I/O操作界面,對類似os包這樣原始的I/O進行封裝。 |
| bufio | 對io包的封裝,提供數(shù)據(jù)緩沖功能,減少大塊數(shù)據(jù)讀寫帶來的開銷。 |
| strcov | 提供將字符串轉(zhuǎn)換為基本數(shù)據(jù)類型,或?qū)⒒緮?shù)據(jù)類型轉(zhuǎn)換為字符串的功能。 |
| os | 提供不依賴平臺的操作系統(tǒng)函數(shù)接口 |
| sync | 實現(xiàn)多線程中鎖機制及其他同步互斥機制 |
| flag | 提供命令行參數(shù)規(guī)則定義和傳入?yún)?shù)解析的功能 |
| encoding/json | 提供對JSON的基本支持 |
| html/template | 實現(xiàn)Web開發(fā)中生成HTML的template模板函數(shù) |
| net/http | 提供HTTP相關(guān)服務(wù) |
| reflect | 實現(xiàn)運行時反射 |
| os/exec | 提供執(zhí)行自定義LINUX命令功能 |
| strings | 處理字符串的函數(shù)集合 |
| bytes | 提供對字節(jié)切片進行讀寫操作的函數(shù) |
| log | 程序中輸出日志 |
自定義包
創(chuàng)建自定義的包需將其存放在GOPATH下的src目錄下,不同包不能放在同一個目錄下,會引發(fā)編譯錯誤。
自定義包使用注意
- 若項目目錄不在
GOPATH環(huán)境變量中,則需將項目移至GOPATH所在目錄中,才能完成編譯。 - 使用
import語句導(dǎo)入時使用的是包所屬文件夾的名稱 - 包中的函數(shù)名首字母要大寫,否則無法在外部調(diào)用。
- 自定義包的包名不必與其所在文件夾名稱保持一致,為了便于維護建議保持一致。
- 調(diào)用自定義包時使用
包名.函數(shù)名的方式調(diào)用