學到什麼
什麼是包?
如果宣告包?
如何匯入包?
原始檔的組成部分?
包內容如何公開和私有?
main 包的作用?
internal 目錄的作用?
多個包出現匯入時,之間的載入順序是什麼?
概念
做個類比理解下包是啥?當電腦上檔案變多時,就會通過目錄區分,將不同的檔案有組織的歸類在不同的目錄下。Go 原始檔也是一樣,可以把不同的檔案放置在不同的目錄中,給目錄取一個別名,就是所說的包名。
下來學習包的使用,就是熟悉 Go 專案中程式碼的組織結構,為了直觀,直接看專案目錄。在上手前首先要掌握 go mod
的使用,不熟悉請前往《環境搭建 - gomod疑惑》。
專案目錄
建立一個 gobasic
的專案目錄,總共包含三個部分:入口檔案、gomod、自定義兩個包。
呼叫順序:main.go
>> b.go
>> a.go
。
下來從這三個檔案入手,開始學習。
包宣告
在原始檔的開頭新增如下程式碼格式:
// a.go
package pkgA
pkgA
為自定義的包名。標準規範中,該命名與原始檔所在目錄名稱相同。入口 “main 函式” 的所在原始檔包名必須設定為 main。
在同一個包(目錄)下,可以建立多個原始檔。
包匯入
宣告一個包後,就可以被其它包匯入使用,格式如下:
// b.go
package pkgB
import "github.com/miaogaolin/gobasic/pkgA"
檔案開頭宣告瞭原始檔所在包為 pkgB
,下來使用 import
關鍵字匯入所依賴的包。如果匯入的是當前專案中的包,引用路徑的規則為 “go.mod” 檔案中設定的 module
值與依賴包的目錄路徑拼接。
我的專案 module 值為:
github.com/miaogaolin/gobasic
匯入之後,就可以使用“包名+點”訪問包內的變數、常量、函式、結構體、介面。
1. 多包匯入
如果有多個包需要匯入時,有兩種方式,第二種為常見方式。
// 第一種
import "gobasic/pkgA"
import "fmt"
// 第二種
import (
"fmt"
"github.com/miaogaolin/gobasic/pkgA"
)
引用後就可以呼叫包內的函式、常量、結構體等等。如果呼叫的函式在同一個包下,就不需要匯入,可以直接呼叫。
2. 簡化匯入
使用“點”匯入的包,在呼叫包內的變數、常量、函式等等,就不需要寫包名。
例:
import . "fmt"
// 不使用“點”匯入
fmt.Println()
// 使用“點”匯入
Println()
3. 別名匯入
給匯入的包可以使用一個別名,這樣如果匯入的多個包時,名稱一樣出現衝突時,就可以取個別名。
例:
import a "exmaple/pkgA"
// a 為別名
a.Func1()
4. 匿名匯入
在匯入包時,如果該包沒有被使用,那編譯器就會報錯。為了不讓報錯,可以使用匿名匯入。那為何不直接刪除呢?是因為想使用包內的 init()
函式,該函式在包被匯入時自動呼叫。
例:
import _ "github.com/go-sql-driver/mysql"
例子中,mysql 這個包內會存在一個 ”init 函式“,該函式的意義表示註冊 mysql 驅動。
原始檔組成
瞭解了包的宣告和引用後,下來看看原始檔的完整組成結構。
例:
// b.go
package pkgB
import (
"fmt"
"github.com/miaogaolin/gobasic/pkgA"
)
var name string
func init() {
name = "B"
}
func PrintName() {
fmt.Println(name)
// 呼叫 pkgA 包中的一個函式
pkgA.PrintName()
}
在這個例子中沒有給出 pkgA
包的程式碼,完整的程式碼前往:
下來根據這個例子講解兩個知識點,繼續往下看。
1. 包的使用
pkgA.PrintName()
這個訪問有個前提,就是函式的命名首字母必須大寫,如果是小寫開頭那隻能在當前包內訪問,而不能被其它包呼叫。
例如, b.go
檔案中的 name
變數就不能被其它包訪問。如果想要訪問,就需要改為 Name
。函式、結構體、介面、常量,也是一樣的。
總結下就是小寫字母開頭私有,大寫字母開頭公有。
2. init 函式
該函式是 Go 語言中的保留函式,當包被匯入後自動執行,不需要主動呼叫。該函式可以在同一個包內的不同原始檔中使用。如果是自定義的函式自然是不行的,相同的函式名稱只能在同一個包內出現一次。
main 包
在學習 Go 語言開始時,寫了一個 “Hello World” 輸出的例子中就有見過,摘取關鍵程式碼如下:
// 入口檔案的包名
package main
// 入口函式
func main() {
...
}
“main 包”和“main 函式” 這兩者的組合,確定了程式的啟動入口。
internal 目錄
這也是 Go 語言中一個特殊的目錄,如果原始檔在 internal
目錄中,那該目錄的父級父級目錄是不能訪問 internal 目錄下的內容的。
這塊我說的是目錄,不是包名。雖然我在前面講了,在規範中,目錄名稱和包名保持相同,但如果不相同語法也是正確的。
回到 internal 目錄,如果 internal 目錄下定義的包名不是 internal 名稱,外部也是不能訪問的。只要目錄名稱不是 internal 就算包名是,外部就能訪問。
舉例展示一個 internal 目錄層級,說明下訪問性。
- exmaple
- dir1
- internal
- main.go
- dir1
在上例中,internal 目錄的父級父級目錄是 exmaple,那在 main.go 檔案中就不能訪問 internal 目錄中的內容。
包的載入順序
這裡我們從 main 包入口開始,main 包匯入 pkg1,pkg1 匯入 pkg2,pkg2 匯入 pkg3。
過程如下:
main 包執行 import pkg1。
pkg1 繼續執行 import pkg2。
pkg2 繼續執行 import pkg3。
pkg3 由於沒有依賴其它包,所以向下執行常量、變數的初始化並執行 init 函式。
接下來執行 pkg2、pkg1、main 包內的常量、變數初始化和執行 init 函式。
最後開始執行 main 函式。
總結
熟悉了包的應用後,下來就要多熟悉熟悉常用包的使用,比如:
fmt
time
strings
regexp
等等…
當然也不是都要熟悉,等基礎知識學完後,找個實際專案實戰,看到哪個包就學哪個就夠了。
本作品採用《CC 協議》,轉載必須註明作者和本文連結