Golang擁有超過100個標準包(可用go list std |wc -l查看)html
任何包系統設計的目的都是簡化大型程序的設計和維護工做,經過將一組相關的特性放進一個獨立的模塊以便於理解和更新,在每一個模塊更新的同時保持和程序中其餘模塊的相對獨立,這種模塊化的特性容許每一個包能被其餘的不一樣項目共享和重用,在項目範圍內、全局範圍內的複用;mysql
每一個包通常都定義了一個不一樣的名字空間用於它內部的每一個標識符的訪問。 每一個名字空間關聯到一個特定的包, 讓咱們給類型、 函數等選擇簡短明瞭的名字, 這樣能夠避免在咱們使用它們的時候減小和其它部分名字的衝突。git
每一個包還經過控制包內名字的可見性和是否導出來實現封裝特性。 經過限制包成員的可見性並隱藏包API的具體實現, 將容許包的維護者在不影響外部包用戶的前提下調整包的內部實現。 經過限制包內變量的可見性, 還能夠強制用戶經過某些特定函數來訪問和更新內部變量, 這樣能夠保證github
內部變量的一致性和併發時的互斥約束。golang
每一個包是由一個全局惟一的字符串所標識的導入路徑定位。 出如今import語句中的導入路徑也是字符串。sql
import ( "fmt" "math/rand" "encoding/json" "golang.org/x/net/html" "github.com/go-sql-driver/mysql" )
在每一個Go源文件的開頭都必須有包聲明語句。 包聲明語句的主要目的是肯定當前包被其它包導入時默認的標識符(也稱爲包名)json
例如, math/rand包的每一個源文件的開頭都包含 package rand 包聲明語句, 因此當你導入這個包, 你就能夠用rand.Int、 rand.Float64相似的方式訪問包的成員。併發
關於默認包名通常採用導入路徑名的最後一段的約定也有三種例外狀況。模塊化
第一個例外, 包對應一個可執行程序, 也就是main包, 這時候main包自己的導入路徑是可有可無的。 名字爲main的包是給go build( §10.7.3) 構建命令一個信息, 這個包編譯完以後必須調用鏈接器生成一個可執行程序。函數
第二個例外, 包所在的目錄中可能有一些文件名是以test.go爲後綴的Go源文件( 譯註:前面必須有其它的字符, 由於以``前綴的源文件是被忽略的) , 而且這些源文件聲明的包名也是以_test爲後綴名的。 這種目錄能夠包含兩種包:一種普通包, 加一種則是測試的外部擴展包。
全部以_test爲後綴包名的測試外部擴展包都由go test命令獨立編譯, 普通包和測試的外部擴展包是相互獨立的。 測試的外部擴展包通常用來避免測試代碼中的循環導入依賴。
第三個例外, 一些依賴版本號的管理工具會在導入路徑後追加版本號信息, 例如"gopkg.in/yaml.v2"。 這種狀況下包的名字並不包含版本號後綴, 而是yaml。
warning:若是咱們想同時導入兩個有着名字相同的包, 例如math/rand包和crypto/rand包, 那麼導入聲明必須至少爲一個同名包指定一個新的包名以免衝突。 這叫作導入包的重命名。
import (
"crypto/rand"
mrand "math/rand" // alternative name mrand avoids conflict
)
warning:匿名導入:
import _ "image/png" // register PNG decoder
1 // The jpeg command reads a PNG image from the standard input 2 // and writes it as a JPEG image to the standard output. 3 package main 4 import ( 5 "fmt" 6 "image" 7 "image/jpeg" 8 _ "image/png" // register PNG decoder 9 "io" 10 "os" 11 )
12 func main() { 13 if err := toJPEG(os.Stdin, os.Stdout); err != nil { 14 fmt.Fprintf(os.Stderr, "jpeg: %v\n", err) 15 os.Exit(1) 16 } 17 } 18 func toJPEG(in io.Reader, out io.Writer) error { 19 img, kind, err := image.Decode(in) //這裏可能須要匿名導入 20 if err != nil { 21 return err 22 } 23 fmt.Fprintln(os.Stderr, "Input format =", kind) 24 return jpeg.Encode(out, img, &jpeg.Options{Quality: 95}) 25 }
下面說說工做機制。 標準庫還提供了GIF、 PNG和JPEG等格式圖像的解碼器,用戶也能夠提供本身的解碼器, 可是爲了保持程序體積較小, 不少解碼器並無被所有包含, 除非是明確須要支持的格式。 image.Decode函數在解碼時會依次查詢支持的格式列表。
每一個格式驅動列表的每一個入口指定了四件事情:格式的名稱;一個用於描述這種圖像數據開頭部分模式的字符串, 用於解碼器檢測識別;一個Decode函數用於完成解碼圖像工做;一個DecodeConfig函數用於解碼圖像的大小和顏色空間的信息。 每一個驅動入口是經過調用image.RegisterFormat函數註冊, 通常是在每一個格式包的init初始化函數中調用, 例如image/png包是這樣註冊的:
1 package png // image/png 2 func Decode(r io.Reader) (image.Image, error) 3 func DecodeConfig(r io.Reader) (image.Config, error) 4 func init() { 5 const pngHeader = "\x89PNG\r\n\x1a\n" 6 image.RegisterFormat("png", pngHeader, Decode, DecodeConfig) 7 }
最終的效果是, 主程序只須要匿名導入特定圖像驅動包就能夠用image.Decode解碼對應格式的圖像!
包是函數和數據的集合。用 package 關鍵字定義一個包。文件名不須要與包名一致,可是你的文件夾必需要與包名字一致,這個很重要,因此爲了簡單起見,名字都是同樣的。包名的約定是使用小寫字符。Go 包能夠由多個文件組成,可是使用相同的;
package 這一行。讓咱們在文件 test1.go 中定義一個叫作 pkg的包。
1 ├── pkg 2 │ └── myprint.go 3 4 //myprint.go 5 package pkg 6 import "fmt" 7 func MyPrintf(in string) { 8 fmt.Printf("%s", in) 9 }
在main包中測試:
1 package main 2 import ( 3 "fmt" 4 "./pkg" //帶上路徑 5 ) 6 7 func main() { 8 pkg.MyPrinf("my pkg test") 9 fmt.Printf("test ok") 10 } 11 12 //測試ok 13 稍做修改: 14 package main 15 import ( 16 "fmt" 17 "pkg" //不帶上路徑 18 ) 19 20 func main() { 21 pkg.MyPrinf("my pkg test") 22 fmt.Printf("test ok") 23 } 24 25 //closure.go:6:2: cannot find package "pkg" in any of: 26 /root/golang/go/src/pkg (from $GOROOT) 27 /root/src/pkg (from $GOPATH) 28 29 # 解決辦法:將pkg包加個軟鏈接到$GOROOT 或$GOPATH