Go语言是使用包来组织源代码的,包(package) 是多个Go源码的集合,是一种高级的代码复用方案。Go语言中为我们提供了很多内置包,如:fmt、os、io等。任何源代码文件必须属于某个包,同时源码文件的第一行有效代码必须是 package packageName 语句,通过该语句声明自己所在的包。
Go 语言的包借助了目录树的组织形式,一般包的名称就是其文件所在目录的名称,虽然Go语言没有强制要求包名必须和其所在的目录名同名,但还是建议包名和所在目录同名,这样结构清晰。
1、包的基本使用
导入包的语法:
import "包的路径" // 双引号
(1)内部模块导入
导入包路径规则:
Go程序首先在 GOROOT/src 目录中寻找包目录,如果没有找到,则会去 GOPATH/src 目录中继续寻找。比如:fmt 包是位于 GOROOT/src 目录的Go语言标准库中的一部分,它将会从该目录中导入。
目录结构:
|- mysite
|-api
| http.go
| rpc.go
| ....
|-main
| main.go
// http.go
package api
import "fmt"
func HttpRequest(){
fmt.Println("HttpRequest方法")
}
// rpc.go
package api
import "fmt"
func RpcRequest(){
fmt.Println("RpcRequest方法")
}
// main.go
package main
import "fmt" //标准库包
import "mysite/api"
func main() {
fmt.Println("hi,gaogao!")
api.HttpRequest()
api.RpcRequest()
}
- 包名一般是小写的,见名知意,包名中不能包好 - 等特殊符号。
- 包名规范上要和所在的目录同名,也可以不同。比如 package api 改为 package NewApi
import "mysite/api" // 注意文件夹api的名字不改动,这里导入的就是文件夹名
func main() {
newApi.HttpRequest() // 使用的package的名字
newApi.RpcRequest()
}
- 包名为main的包为应用程序的入口包,编译不包含main包的源码文件时不会得到可执行文件。
- 一个文件夹下的所有源码文件只能属于同一个包,同样属于同一个包的源码文件不能放在多个文件夹下。
- 一个包下的不同文件不能含有同名函数。
- 如果想在一个包中引用另外一个包里的标识符(如变量、常量、类型、函数等)时,该标识符必须是对外可见的(public),在Go语言中只需要将标识符的首字母大写就可以让标识符对外可见了。
- 环境变量 GO1111MODULE=off
- 将 mysite 剪切到 src 外的任何位置,都会导致包失败。
(2)外部模块导入
当需要一个外部包时需要下载到本地,命令是:
go get "远程包"
go get命令会依赖git clone拉取下载指定的包,并将下载的包进行编译,然后安装到 $GOPATH/src 下。比如:
package main
import (
"fmt"
"mysite/api" // 内部包导入
)
import "github.com/jinzhu/now" // 外部包导入
func main() {
api.HttpRequest()
api.RpcRequest()
fmt.Println(now.BeginningOfMinute())
}
远程包:
go get "github.com/jinzhu/now"
git-clone拉取下载存放在gopath/src目录:
然后编译器再执行import “github.com/jinzhu/now”就可以从 $GOPATH/src 下搜索找到这个包了。
2、包的导入格式
// 一次导入多个包
import (
"fmt"
"github.com/jinzhu/now"
"hi/day06/api"
)
// 设置包的别名
import sbh "github.com/jinzhu/now"
fmt.Println(sbh.BeginningOfDay())
// 省略引用格式
import . "mysite/api"
HttpRequest()
/*
匿名导入:在引用某个包时,如果只是希望执行包初始化的 `init` 函数,而不使用
包内部的数据时,可以使用匿名引用格式。
*/
import _ "包名"
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
3、包的加载顺序
init() 函数会在每个包完成初始化后自动执行,并且执行优先级比 main 函数高。
注意:
- 一个包可以有多个 init 函数,包加载时会执行全部的 init 函数,但并不能保证执行顺序,所以不建议在一个包中放入多个 init 函数,将需要初始化的逻辑放到一个 init 函数里面。
- 包不能出现环形引用的情况,比如包 a 引用了 包 b,包b引用了 包 c,如果包 c又引用了包a,则编译不能通过。
- 包的重复引用是允许的,比如 包 a引用了包b和包c,包b和包c都引用了包d。这种场景相当于重复引用了包 d,这种情况是允许的,并且Go编译器保证 包d的init函数只会执行一次。
- init() 函数没有参数也没有返回值。init() 函数在程序运行时自动被调用执行,不能在代码中主动调用它