Go语言是使用包来组织源代码的,包(package) 是多个Go源码的集合,是一种高级的代码复用方案。Go语言中为我们提供了很多内置包,如:fmt、os、io等。任何源代码文件必须属于某个包,同时源码文件的第一行有效代码必须是 package packageName 语句,通过该语句声明自己所在的包。

Go 语言的包借助了目录树的组织形式,一般包的名称就是其文件所在目录的名称,虽然Go语言没有强制要求包名必须和其所在的目录名同名,但还是建议包名和所在目录同名,这样结构清晰。

1、包的基本使用

导入包的语法:

  1. import "包的路径" // 双引号

(1)内部模块导入

导入包路径规则:
Go程序首先在 GOROOT/src 目录中寻找包目录,如果没有找到,则会去 GOPATH/src 目录中继续寻找。比如:fmt 包是位于 GOROOT/src 目录的Go语言标准库中的一部分,它将会从该目录中导入。
目录结构:

  1. |- mysite
  2. |-api
  3. | http.go
  4. | rpc.go
  5. | ....
  6. |-main
  7. | main.go

  1. // http.go
  2. package api
  3. import "fmt"
  4. func HttpRequest(){
  5. fmt.Println("HttpRequest方法")
  6. }
  7. // rpc.go
  8. package api
  9. import "fmt"
  10. func RpcRequest(){
  11. fmt.Println("RpcRequest方法")
  12. }
  13. // main.go
  14. package main
  15. import "fmt" //标准库包
  16. import "mysite/api"
  17. func main() {
  18. fmt.Println("hi,gaogao!")
  19. api.HttpRequest()
  20. api.RpcRequest()
  21. }
  1. 包名一般是小写的,见名知意,包名中不能包好 - 等特殊符号。
  2. 包名规范上要和所在的目录同名,也可以不同。比如 package api 改为 package NewApi
  1. import "mysite/api" // 注意文件夹api的名字不改动,这里导入的就是文件夹名
  2. func main() {
  3. newApi.HttpRequest() // 使用的package的名字
  4. newApi.RpcRequest()
  5. }
  1. 包名为main的包为应用程序的入口包,编译不包含main包的源码文件时不会得到可执行文件。
  2. 一个文件夹下的所有源码文件只能属于同一个包,同样属于同一个包的源码文件不能放在多个文件夹下。
  3. 一个包下的不同文件不能含有同名函数。
  4. 如果想在一个包中引用另外一个包里的标识符(如变量、常量、类型、函数等)时,该标识符必须是对外可见的(public),在Go语言中只需要将标识符的首字母大写就可以让标识符对外可见了。
  5. 环境变量 GO1111MODULE=off
  6. 将 mysite 剪切到 src 外的任何位置,都会导致包失败。

(2)外部模块导入

当需要一个外部包时需要下载到本地,命令是:

  1. go get "远程包"

go get命令会依赖git clone拉取下载指定的包,并将下载的包进行编译,然后安装到 $GOPATH/src 下。比如:

  1. package main
  2. import (
  3. "fmt"
  4. "mysite/api" // 内部包导入
  5. )
  6. import "github.com/jinzhu/now" // 外部包导入
  7. func main() {
  8. api.HttpRequest()
  9. api.RpcRequest()
  10. fmt.Println(now.BeginningOfMinute())
  11. }

远程包:
10.1、package - 图1

  1. go get "github.com/jinzhu/now"

git-clone拉取下载存放在gopath/src目录:
10.1、package - 图2
然后编译器再执行import “github.com/jinzhu/now”就可以从 $GOPATH/src 下搜索找到这个包了。

2、包的导入格式

  1. // 一次导入多个包
  2. import (
  3. "fmt"
  4. "github.com/jinzhu/now"
  5. "hi/day06/api"
  6. )
  7. // 设置包的别名
  8. import sbh "github.com/jinzhu/now"
  9. fmt.Println(sbh.BeginningOfDay())
  10. // 省略引用格式
  11. import . "mysite/api"
  12. HttpRequest()
  13. /*
  14. 匿名导入:在引用某个包时,如果只是希望执行包初始化的 `init` 函数,而不使用
  15. 包内部的数据时,可以使用匿名引用格式。
  16. */
  17. import _ "包名"
  18. import (
  19. "database/sql"
  20. _ "github.com/go-sql-driver/mysql"
  21. )

3、包的加载顺序

init() 函数会在每个包完成初始化后自动执行,并且执行优先级比 main 函数高。
注意:

  1. 一个包可以有多个 init 函数,包加载时会执行全部的 init 函数,但并不能保证执行顺序,所以不建议在一个包中放入多个 init 函数,将需要初始化的逻辑放到一个 init 函数里面。
  2. 包不能出现环形引用的情况,比如包 a 引用了 包 b,包b引用了 包 c,如果包 c又引用了包a,则编译不能通过。
  3. 包的重复引用是允许的,比如 包 a引用了包b和包c,包b和包c都引用了包d。这种场景相当于重复引用了包 d,这种情况是允许的,并且Go编译器保证 包d的init函数只会执行一次。
  4. init() 函数没有参数也没有返回值。init() 函数在程序运行时自动被调用执行,不能在代码中主动调用它

10.1、package - 图3