- Go语言
- 8. 包管理
- 8.1 工作区
- 8.1.1 工作区结构
- 8.1.2 GOPATH
- 8.1.3 GOROOT
- 8.1.4 GOBIN
- 8.2 包的声明
- 8.3 包的导入
- 8.3.1 导入声明
- 8.3.2 远程导入
- 8.3.3 别名导入
- 8.3.4 匿名导入
- 8.4 main包
- 8.5 包的初始化
- 8.6 包的命名
- 8.7 依赖包管理
- 8.8 Go语言命名规范
- 8.8.1 驼峰式命名法
- 8.8.2 导出标识符
- 8.10 知识拓展(标准包简介)
- 8.10.1 unsafe
- 8.10.2 os-os/exec-syscall
- 8.10.3 archive/tar和/zip-compress
- 8.10.4 fmt-io-bufio-path/filepath-flag
- 8.10.5 strings-strconv-unicode-regexp-bytes
- 8.10.6 math-math/cmath-math/rand-sort-math/big
- 8.10.7 container-/list-ring-heap
- 8.10.8 time-log
- 8.10.9 encoding/Json-encoding/xml-text/template
- 8.10.10 net-net/http-html
- 8.10.11 runtime
- 8.10.12 reflect
Go语言包的主要作用是把功能相似或相关的代码组织在同一个包中,以方便查找和使用。
8.1 工作区Go语言中没有工程文件的概念,而是通过目录结构来体现工程的结构关系。Go代码必须放在工作区中。
8.1.1 工作区结构工作区其实就是一个对应于特定工程的目录,它应包含三个子目录:src目录、pkg目录和bin目录。
GO工作区: ├─bin │ ... ├─pkg │ └─windows_amd64 │ ├─github.com │ ... └─src ├─github.com ...
各个目录作用
-
src目录
用于以代码包的形式组织并保存Go源码文件(如.go、.c、.h、.s等),同时也是Go编译时查找代码的地方
-
pkg目录
用于存放经由go get/install命令构建安装后的代码包的“.a”归档文件,也是编译生成的lib文件存储的地方。
-
bin目录
与pkg目录类似,在通过go get/install命令完成安装后,保存由Go命令源码文件生成的可执行文件。
目录src用于包含所有的源代码,是Go命令行工具一个强制的规则,而pkg和bin则无须手动创建,必要时Go命令行工具在构建过程中会自动创建这些目录。
8.1.2 GOPATHGOPATH可以理解为工作目录或工作区,是平时接触最多的一个环境变量。它可以是一个目录,也可以是多个目录路径,每个目录代表一个工作区。
Go的所有操作(编码、依赖管理、构建、测试、安装等)基本上都是围绕GOPATH来进行的。GOPATH必须要设置,但并不是固定不变的,GOPATH的目的是为了告知Go需要代码的时候去哪里查找。需要注意的是,这里的代码包括本项目和引用外部项目的代码。GOPATH可以随着项目的不同而重新设置。
在实际开发环境中,工作目录往往有多个。这些工作目录的目录路径都需要添加至GOPATH。
为了能够构建这个工程,需要先把所需工程的根目录加入到环境变量GOPATH中。否则,即使处于同一工作目录(工作区),代码之间也无法通过绝对代码包路径完成调用。
使用命名行添加GOPATH
查看当前的GOPATH
临时添D:NewProject目录到GOPATH可使用set命令,Windows下使用分号作为分隔符:
使用set添加GOPATH,添加的是临时变量,当cmd窗口结束时,该变量就会结束。如果想要永久添加一个GOPATH路径,可以使用setx,只需要将如上命令的“set”更换成“setx”即可。
如果GOPATH设置了多个工作区,那么查找依赖包时是以怎样的顺序进行呢?例如包a依赖包b,包b依赖包c,那么会先查找c包。那在工作区是如何查找这个依赖包c的呢?
→ 首先,在查找依赖包的时候,总是会先查找GOROOT目录,也就是Go语言的安装目录,如果没有找到依赖的包,才到工作区去找相应的包。在工作区中是按照设置的先后顺序来查找的,也就是会从第一个开始依次查找,如果找到就不再继续,如果没有找到就报错。
go get会下载代码包到src目录,但是只会下载到第一个工作区目录。
8.1.3 GOROOTGOROOT是Go语言的程序安装目录,并非必须要设置的。GOROOT的目的就是告知Go当前的安装位置,编译的时候从GOROOT去找SDK的system library。
Windows下查看GOROOT
8.1.4 GOBINGOBIN目录一般为GOPATH的可执行文件放置目录,一般指bin。默认安装的Go语言是没有设置GOBIN目录的。
只有当环境变量GOPATH中只包含一个工作区的目录路径时,go install命令才会把命令源码安装到当前工作区的bin目录下。若环境变量GOPATH中包含多个工作区的目录路径,像这样执行go install命令就会失效,此时必须设置环境变量GOBIN。
8.2 包的声明包是结构化代码的一种方式:每个程序都由包(通常简称为pkg)的概念组成,可以使用自身的包或者从其他包中导入内容。
- Go语言的源代码以代码包为基本的组织单位
- 在文件系统中,代码包是与文件目录一一对应的,文件目录的子目录也就是代码包的子包。
- 在工作区中,一个代码包的导入路径实际上就是从src子目录到该包的实际存储位置的相对路径。
每一个Go源文件的第一行都需要声明包的名称,声明一个包使用关键字package。
每个Go文件都属于且仅属于一个包。一个包可以由许多以“.go”为扩展名的源文件组成,因此文件名和包名一般来说都是不相同的。
代码包的名称一般会与源码文件所在的目录同名,如果不同名,在构建和安装的过程中会以代码包的名称为准。
在Go语言中,代码包中的源码文件名可以是任意的。这些任意名称的源码文件都必须以包声明语句作为文件中的第一行,每个包都对应一个独立的名字空间。
包中成员以名称首字母大小写决定访问权限:
- public:首字母大写,可被包外访问
- private:首字母小写,仅包内成员可以访问
8.3 包的导入【注意】同一个目录下不能定义不同的package。
在Go语言程序中,每个包都有一个全局唯一的导入路径。
8.3.1 导入声明导入包需要使用关键字import,它会告诉编译器你想引用该位置的包中的代码,包的路径可以是相对路径,也可以是绝对路径。
// 声明方式一 import "fmt" import "time" // 声明方式二 import ( "fmt" "time" )
如上所示的标准包会在Go语言的安装位置下的src目录中找到,而其他包都需要在GOPATH环境变量中的目录下寻找,GOPATH指定的这些目录就是开发者的个人工作空间。
如果在GOPATH中仍然没有找到所需的包,编译器在编译或运行时就会报错。
8.3.2 远程导入Go语言不仅支持调用本地包,还支持调用远程服务器的包,例如托管在GitHub上的一个非常热门的Web框架gin,可以通过远程导入的方式将其导入使用。
使用go get 命令下载远程包
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run()
}
成功返回
说明有人请求了服务
8.3.3 别名导入在导入包时,可以指定包成员的访问方式,例如对包进行重命名,也就是使用别名的方式,避免导入两个相同包名的不同包而发生冲突。
import print "fmt"
package main
import (
print "fmt"
date "time"
)
func main() {
print.Println("当前日期为:", date.Now().Day())
}
8.3.4 匿名导入
在Go语言中,如果导入了一个包而不使用,在编译时会产生“unused import”的编译错误。有时可能需要导入一个包,但不会引用到这个包的标识符,在这种情况下可以使用包的匿名导入的方式,就是使用下划线“_”来重命名该包
import _“fmt”
性能分析案例
package main
import (
"net/http"
_ "net/http/pprof"
)
func ping(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("{"message":"pong"}"))
}
func main() {
http.HandleFunc("/ping", ping)
http.ListenAndServe(":8080", nil)
}
8.4 main包
在Go语言中,命名为main的包具有特殊的含义。Go语言的编译程序会试图把这种名字的包编译为二进制可执行文件。所有用Go语言编译的可执行程序都必须有一个名叫main的包,main包有且仅有一个。
当编译器发现某个包的名字为main时,它一定也会发现名为main()的函数,否则不会创建可执行文件。main()函数是程序的入口,所以,如果没有这个函数,程序就没有办法开始执行。程序编译时,会使用声明main包的代码所在的目录名称作为可执行文件的文件名。
一个应用程序可以包含不同的包,而且即使你只使用main包也不必把所有的代码都写在一个巨大的文件里,你可以用一些较小的文件,并且在每个文件非注释的第一行都使用package main来指明这些文件都属于main包。如果你打算编译包名不为main的源文件,如mypack,编译后产生的对象文件将会是mypack.a而不是可执行程序。
8.5 包的初始化注意,所有的包名都应该使用小写字母。
在Go语言里面有两个保留的函数:init函数(能够应用于所有的package)和main函数(只能应用于package main)。这两个函数在定义时不能有任何的参数和返回值。
建议用户在一个package中,每个文件只写一个init函数。
每个package中的init函数都是可选的,但package main就必须包含一个main函数。
每个包可以包含任意多个init(),这些函数都会在程序执行开始的时候被调用。所有被编译器发现的init()都会安排在main()之前执行。init()用于设置包、初始化变量或者其他要在程序运行前优先完成的引导工作。
程序的初始化和执行都起始于main包。如果main包还导入了其他的包,那么就会在编译时将它们依次导入。
当一个包被导入时,如果该包还导入了其他的包,那么会先将其他的包导入进来,然后再对该包的包级常量和变量进行初始化,最后执行init函数(如果存在)。
8.6 包的命名每个包都有一个包名,包名一般是短小的名字,在包的声明处指定。通常来说,默认的包名就是包导入路径名的最后一段,因此即使两个包的导入路径不同,它们依然可能有一个相同的包名。
包的命名规则:
- 同目录下的源码文件的代码包声明语句要一致。
- 源码文件声明的代码包的名称可以与其所在的目录的名称不同。
Go在1.5版本引入了vendor属性(默认关闭,需要设置Go环境变量GO15VENDOREXPERIMENT=1),并在1.6版本中默认开启了vendor属性。
vendor属性就是让Go在编译时,优先从项目源码树根目录下的vendor目录查找代码(可以理解为GOPATH),如果vendor中有,则不再去GOPATH中查找。
有时候依赖包多了,逐个拷贝是非常费时费力的,因此在Go 1.11之后,官方发布了支持的版本管理工具mod。使用go mod命令即可运行mod工具。
mod命令:
| download | 下载依赖的module到本地cache |
|---|---|
| edit | 编辑go.mod文件 |
| graph | 打印模块依赖图 |
| init | 在当前文件夹下初始化一个新的module,创建go.mod文件 |
| tidy | 增加丢失的module,去掉未用的module |
| vendor | 将依赖复制到vendor下 |
| verify | 校验依赖 |
| why | 解释为什么需要依赖 |
当需要将所有外部依赖拷贝到vendor目录下时,只需要运行一条命令:
go mod vendor8.8 Go语言命名规范 8.8.1 驼峰式命名法
Go语言中,我们推荐使用驼峰式命名法来对各类标识符(变量、常量和结构体等)和文件进行命名。
-
小驼峰命名法
第一个单词以小写字母开始,其他单词的首字母则需要大写
var sumNum int var firstName string var isValid bool var printValue func()
-
大驼峰命名法
每一个单词的首字母都采用大写
var SumNum int var FirstName string var IsValid bool var PrintValue func()
在进行测试用例的编写时,文件的命名不遵循驼峰式命名法,而是以xx_test.go的格式进行命名。
测试文件scan_test.go中的TestScan函数。
8.8.2 导出标识符Go语言中标识符根据首字母的大小写来确定可以访问的权限。
如果首字母大写,则表示可以被其他包中的代码访问(可被导出);如果首字母小写,则表示该标识符只能在本包中使用(不可导出)。
8.10 知识拓展(标准包简介) 8.10.1 unsafe通俗认识:首字母大写的标识符是公有的,首字母小写的标识符是私有的。
unsafe包含了一些打破Go语言“类型安全”的命令,一般的程序不会使用它,可用于C/C++ 程序的调用中。
8.10.2 os-os/exec-syscallos:提供一个平台无关性的操作系统功能接口,采用类Unix设计,隐藏了不同操作系统间的差异,让不同的文件系统和操作系统对象表现一致。
os/exec:提供给我们运行外部操作系统命令和程序的方式。
syscall:底层的外部包,提供了操作系统底层调用的基本接口。
压缩(解压缩)文件功能。
8.10.4 fmt-io-bufio-path/filepath-flagfmt:提供了格式化输入输出功能。
io:提供了基本输入输出功能,大多数是围绕系统功能的封装。
bufio:缓冲输入输出功能的封装。
path/filepath:用来操作在当前系统中的目标文件名路径。
flag:对命令行参数的操作。
strings:提供对字符串的操作。
strconv:提供将字符串转换为基础类型的功能。
unicode:为unicode型的字符串提供特殊的功能。
regexp:正则表达式功能。
bytes:提供对字符型分片的操作。
math:基本的数学函数。
math/cmath:对复数的操作。
math/rand:伪随机数生成。
sort:为数组排序和自定义集合。
math/big:大数的实现和计算。
实现对集合的操作。
list:双链表。
ring:环形链表。
time:日期和时间的基本操作。
log:记录程序运行时产生的日志。
encoding/Json:读取并解码和写入并编码JSON数据。
encoding/xml:简单的XML 1.0解析器。
text/template:生成像HTML一样的数据与文本混合的数据驱动模板。
net:网络数据的基本操作。
http:提供了一个可扩展的HTTP服务器和客户端,解析HTTP请求和回复。
html:HTML5解析器。
Go程序运行时的交互操作,例如垃圾回收和协程创建。
8.10.12 reflect实现通过程序运行时反射,让程序操作任意类型的变量。



