3.1 Go語(yǔ)言從入門到精通:包
作者:xcbeyond
瘋狂源自夢(mèng)想,技術(shù)成就輝煌!微信公眾號(hào):《程序猿技術(shù)大咖》號(hào)主,專注后端開(kāi)發(fā)多年,擁有豐富的研發(fā)經(jīng)驗(yàn),樂(lè)于技術(shù)輸出、分享,現(xiàn)階段從事微服務(wù)架構(gòu)項(xiàng)目的研發(fā)工作,涉及架構(gòu)設(shè)計(jì)、技術(shù)選型、業(yè)務(wù)研發(fā)等工作。對(duì)于Java、微服務(wù)、數(shù)據(jù)庫(kù)、Docker有深入了解,并有大量的調(diào)優(yōu)經(jīng)驗(yàn)。
Go 語(yǔ)言像 Java 語(yǔ)言一樣都擁有包的概念,通過(guò)使用包來(lái)組織源代碼。包(package)是多個(gè) Go 源碼的集合,是一種高級(jí)的代碼復(fù)用方案。Go語(yǔ)言中為我們提供了很多內(nèi)置包,如 fmt、os、io 等。
任何 Go 源代碼文件都必屬于某個(gè)包,同時(shí)源碼文件的第一行有效代碼必須是package pacakgeName 語(yǔ)句,通過(guò)該語(yǔ)句聲明自己所在的包。
1、包的概述
Go 語(yǔ)言的包借助了目錄樹(shù)的組織形式,一般包的名稱就是其源文件所在目錄的名稱,雖然 Go 語(yǔ)言沒(méi)有強(qiáng)制要求包名必須和其所在的目錄名同名,但還是建議包名和所在目錄同名,這樣代碼結(jié)構(gòu)會(huì)更加清晰。
1.1 包的定義
包的聲明如下:
package <package_name>
注意:包聲明必須在 go 源文件的第一行定義。
例如,在 GOPATH/src/a/b/下定義包 c,則在 GOPATH/src/a/b/目錄下的所有 go 源文件第一行需要聲明
package c,而不是聲明為 package a.b.c 或 package a/b/c,但在導(dǎo)入包時(shí),需要帶上路徑 import
a/b/c。(注意:這與 Java 語(yǔ)言中包的用法差異很大)
1.2 包的命名規(guī)范
清晰的代碼結(jié)構(gòu),少不了規(guī)范命名,包的命名更為重要。規(guī)范命名的目的是為了讓你的代碼更清晰,別人讀起來(lái)更快上手。包命名規(guī)范推薦如下:
包名一般小寫(xiě),使用一個(gè)簡(jiǎn)短且有意義的單詞或縮寫(xiě)。
包名與所在包的目錄同名,看目錄知包名。
包名一般采用域名作為包目錄結(jié)構(gòu),以此來(lái)確保唯一性。如:GOPATH/src/github.com/xcbeyond/projectName/...。
程序入口包名必須是 main 包。
同一目錄下的所有 go 源文件都屬于同一個(gè)包。
2、包的使用
有了包的存在,必然少不了對(duì)其導(dǎo)入,可借助 import 關(guān)鍵字導(dǎo)入想使用的包。具體語(yǔ)法如下:
import “包路徑”
注意:
import 導(dǎo)入語(yǔ)句需放在源碼文件開(kāi)頭的包聲明語(yǔ)句的下面。
導(dǎo)入的包名需要使用雙引號(hào) "" 括起來(lái)。
包名是從 GOPATH/src/ 后開(kāi)始計(jì)算的,使用 / 進(jìn)行路徑分隔。
通常導(dǎo)入包有單行導(dǎo)入和多行導(dǎo)入兩種方式。
單行導(dǎo)入:
import “包1路徑”
import “包2路徑”
多行導(dǎo)入:
import (
“包1路徑”
“包2路徑”
)
2.1 導(dǎo)入路徑
關(guān)于包的導(dǎo)入路徑有兩種方式,分別是全路徑導(dǎo)入和相對(duì)路徑導(dǎo)入。
全路徑導(dǎo)入:
包的絕對(duì)路徑就是 GOROOT/src/ 或 GOPATH/src/ 后面包的存放路徑,如下所示:
import “l(fā)ab/test”
import “database/sql/driver”
import “database/sql”
其中:
test 包是自定義的包,其源碼位于 GOPATH/src/lab/test 目錄下。
driver 包的源碼位于 GOROOT/src/database/sql/driver 目錄下。
sql 包的源碼位于 GOROOT/src/database/sql 目錄下。
相對(duì)路徑導(dǎo)入:
相對(duì)路徑只能用于導(dǎo)入GOPATH 下的包,標(biāo)準(zhǔn)包的導(dǎo)入只能使用全路徑導(dǎo)入。
例如,包 a 的所在路徑是 GOPATH/src/lab/a,包 b 的所在路徑為 GOPATH/src/lab/b,如果在包 b 中導(dǎo)入包 a ,則可以使用相對(duì)路徑導(dǎo)入方式。示例如下:
// 相對(duì)路徑導(dǎo)入
import “…/a”
2.2 包的引用
包的引用有四種格式,下面以 fmt 包為例分別進(jìn)行說(shuō)明。
標(biāo)準(zhǔn)引用格式
import "fmt"
1
用 fmt. 作為前綴來(lái)使用 fmt 包中的方法,這是最為常用的一種方式。
示例如下:
package main
import "fmt"
func main() {
fmt.Println("Hello World!")
}
自定義別名引用格式
在導(dǎo)入包的時(shí)候,我們可以為導(dǎo)入的包設(shè)置別名,簡(jiǎn)化使用:
import F "fmt"
1
其中 F 就是 fmt 包的別名,可以用 F.來(lái)代替標(biāo)準(zhǔn)引用格式的 fmt. 來(lái)作為前綴使用 fmt 包中的方法。
示例如下:
package main
import F "fmt"
func main() {
F.Println("Hello World!")
}
省略引用格式
import . "fmt"
1
相當(dāng)于把 fmt 包直接合并到當(dāng)前源文件中,在使用 fmt 包內(nèi)的方法是可以不用加前綴 fmt.而直接引用。
示例如下:
package main
import . "fmt"
func main() {
Println("Hello World!")
}
匿名引用格式
在引用某個(gè)包時(shí),如果只是希望執(zhí)行包初始化的 init 函數(shù),而不使用包內(nèi)部的數(shù)據(jù)時(shí),可以使用匿名引用格式:
import _ "fmt"
使用標(biāo)準(zhǔn)格式引用包,但代碼中卻沒(méi)有使用包,編譯器是會(huì)報(bào)錯(cuò)。如果包中有 init 初始化函數(shù),則通過(guò)import _ "包的路徑" 這種方式引用包,僅執(zhí)行包的初始化函數(shù),即使包沒(méi)有 init 初始化函數(shù),也不會(huì)引發(fā)編譯器報(bào)錯(cuò)。
示例如下:
package main
import (
_ "database/sql"
"fmt"
)
func main() {
Println("Hello World!")
}
2.3 包的初始化
通過(guò)前面的學(xué)習(xí)相信大家已經(jīng)大體了解了 Go 程序的啟動(dòng)和加載過(guò)程,在執(zhí)行 main 包的 main 函數(shù)之前, Go 引導(dǎo)程序會(huì)先對(duì)整個(gè)程序的包進(jìn)行初始化。整個(gè)執(zhí)行的流程如下圖所示:
Go語(yǔ)言包的初始化有如下特點(diǎn):
包初始化程序從 main 函數(shù)引用的包開(kāi)始,逐級(jí)查找包的引用,直到找到?jīng)]有引用其他包的包,最終生成一個(gè)包引用的有向無(wú)環(huán)圖。
Go 編譯器會(huì)將有向無(wú)環(huán)圖轉(zhuǎn)換為一棵樹(shù),然后從樹(shù)的葉子節(jié)點(diǎn)開(kāi)始逐層向上對(duì)包進(jìn)行初始化。
單個(gè)包的初始化過(guò)程如上圖所示,先初始化常量,然后是全局變量,最后執(zhí)行包的 init 函數(shù)。