包(package)
Go 的源码复用建立在包( package )基础之上。 Go 的入口 main()函数所在的包( package )叫 main, main 包想要引用别的代码,必须同样以包的方式进行引用,本章内容将详细讲解如何导出包的内容及如何导入其他包。
Go 的包与文件夹一一对应,所有与包相关的操作,必须依赖于工作目录(GOPATH )。
8.1 工作目录(GOPATH)
GOPATH是Go语言中使用的一个环境变量 ,它使用绝对路径提供项目的工作目录。
GOPATH 适合处理大量Go 源码、 多个包组合而成的复杂工程。
8.1.1 使用命令行查看GOPATH信息
在命令行中运行 go env
后,命令行将提示以下信息:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gu6QxnKC-1668395343035)(C:/Users/LENOVO/AppData/Roaming/Typora/typora-user-images/image-20221111154652763.png)]
命令行输入结果说明如下:
- 第1行,执行go env命令,将输入当前Go开发包的环境变量状态。
- 第3行,GOARCH表示目标处理器架构
- 第4行,GOBIN表示编译器和链接器的安装位置。
- 第16行,GOOS表示目标操作系统。
- 第17行,GOPATH表示Go的工作目录。
- 第20行,GOROOT表示Go的开发包安装目录。
Go 1.8版本前,GOPATH环境变量默认是空的。从Go 1.8版本开始,Go的开发包在安装完成后,将GOPATH赋予了一个默认目录,参见下表:
平台 | GOPAT默认值 | 举例 |
---|---|---|
Windows平台 | %USEPROFILE%/go | C:\Users\用户名\go |
Unix平台 | $HOME/go | /home/用户名/go |
8.1.2 使用GOPATH的工程结构
在GOPATH 指定的工作目录下,代码总是会保存在 G O P A T H / s r c 目 录 ∗ ∗ 。 在 工 程 ∗ ∗ 经 过 g o b u i l d 、 g o i n s t a l l 或 g o g e t 等 指 令 ∗ ∗ 后 , 会 将 ∗ ∗ 产 生 的 二 进 制 可 执 行 文 件 放 在 GOPATH/src 目录** 。在工程**经过go build、go install 或go get 等指令**后,会将**产生的二进制可执行文件放在 GOPATH/src目录∗∗。在工程∗∗经过gobuild、goinstall或goget等指令∗∗后,会将∗∗产生的二进制可执行文件放在GOPATH/bin目录下,生成的中间缓存文件会被保存在$GOPATH/pkg下。
如果需要将整个源码添加到版本管理工具 (Version Control System, VCS )中时,只需要添加$GOPATH/src 目录的源码即可。 bin和pkg 目录的内容都可由 src 目录生成。
8.1.3 设置和使用GOPATH
以Linux平台为例,设置和使用GOPATH的方法。
-
设置当前目录为GOPATH
选择一个目录,在目录的命令行中执行以下命令:
export GOPATH=`pwd`
反引号+pwd表示命令行替换,将获得返回当前目录的值。例如,当前目录为”/home/david/go“,那么`pwd`返回的值就是”/home/david/go“。
使用export指令可以将当前目录的值设置到环境变量GOPATH中。
-
建立GOPATH的源码目录
使用下面的命令创建GOPATH中的src目录,且在src下建立一个hello目录,该目录保存源码。
mkdir -p /src/hello
mkdir指令的 -p 可以连续创建一个路径。
-
添加main.go源码文件
使用Linux编辑器将以下源码保存为main.go,并保存到$GOPATH/src/hello目录下。
package main import "fmt" func main() { fmt.Println("hello") }
-
编译源码并运行
此时我们已经设定了GOPATH,因此在Go语言中可以通过GOPATH找到工程的位置。
在命令行中执行以下编译代码:
go install hello
编译完成的可执行文件会保存在$GOPATH/bin 目录下。
bin 目录中执行 ./hello ,命令行输出: hello
8.2 创建包package
包是多个Go源码的集合,是一种高级的代码复用方案,Go语言默认为我们提供了很多包,如fmt、os、io包等,开发者可以根据自己需要创建自己的包。
package 包名
包的特性:
- 一个目录下的同级文件属于一个包
- 包名可以与目录名不同
- 包名为main的包为应用程序入口包,编译源码没有main包,将无法编译输出可执行文件。
8.3 导出标识符
在Go语言中,如果想在 一个包里引用另外一个包里的标识符(如类型、变量 、常量等)时,必须首先将被引用的标识符导出,将要导出的标识符的首字母大写就可以让引用者可以访问这些标识符了。
8.3.1 导出包内标识符
函数、结构体、变量等首字母大写,即可导出标识符,例如:
package my
var myVar = 100
const Myconst = "hello"
type Mystruct struct{}
此时, MyConst和MyStruct 可以被外部访问,而 myVar由于首字母是小写,因此只能在 mypkg 包内使用,不能被外部包引用。
8.3.2 导出结构体及接口成员
在被导出的结构体或接口中 ,如果它们的字段或方法首字母是大写 ,外部可以访问这些字段和方法,代码如下:
type MyStruct struct {
ExportField int //包外可访问字段
privateField int //仅限包内访问的字段
}
type MyInterface interface {
ExportMethod() //包外可以访问的方法
privateMethod() //仅限包内访问的方法
}
8.4 导入包
要引用其他包的标识符,可以使用 import 关键字,导入的包名使用双引号包围,包名是从 GOPATH 开始计算的路径,使用“/”进行路径分隔。
8.4.1 默认导入的方法
导入有两种基本格式,即单行导入和多行导入,效果一致。
-
单行导入
格式如下:
import “包1”
import “包2”
-
多行导入
格式如下:
import(
“包1”
“包2”
…
)
8.4.2 导入包后使用自定义引用的包名
在默认导入包的基础上,在导入包路径前添加标识符即可形成自定义引用包,格式如下:
customName "path/to/package ”
-
path/to/package 为要导入的包路径。
-
customName 自定义的包名。
8.4.3 匿名导入包
如果只希望导入包,而不使用任何包内的结构和类型,也不调用包内的任何函数时,可以使用匿名导入包,格式如下:
import (
_ “path/to/package”
)
- path/to/package: 表示导入的包名
- 下划线:表示匿名导入包
匿名导入的包与其他方式导入包一样会让导入包编译到可执行文件中,同时,导入包也会触发 init() 函数调用。
8.4.4 包在程序启动前的初始化入口:init
在某些需求的设计上需要在程序启动时统一调用程序引用到的所有包的初始化函数,如果需要通过开发者手动调用这些初始化函数,那么这个过程可能会发生错误或者遗漏。
我们希望在被调用的包内部,由包的编写者获得代码启动的通知,在程序启动时做一些自己包内代码的初始化工作。
Go语言为以上问题提供了一个非常方便的特性 init()函数。
init函数特性如下:
-
每个源码可以使用1个init函数。
-
init函数会在程序执行前(main函数执行前)被自动调用。
-
调用顺序为main()中引用包,以深度优先顺序初始化。
例如,假设有这样的包引用关系:main—>A—>B—>C,那么这些包的init函数调用顺序为:
C.init—>B.init—>A.init—>main