0%

Go语言学习笔记7-包(package)

[TOC]

终于学到包了,前面有忘得差不多了,特别是接口,晕晕乎乎的,期待学完之后的项目实战

Go 语言的源码复用建立在包(package) 基础之上。Go 语言的入口main() 函数所在的包叫main,main 包想要引用别的代码,必须同样以包的方式进行。

Go 于洋的包与文件夹一一对应,所有与包相关的操作,必须依赖工作目录(GOPATH)

GOPATH详解

GOPATH 是Go 语言中使用的一个环境变量,它使用绝对路径提供项目的工作目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
☁  ~  go env // 执行 go env 指令,将输出当前 Go 开发包的环境变量状态。 
GOARCH="amd64" // GOARCH 表示目标处理器架构
GOBIN="" // GOBIN 表示编译器和链接器的安装位置。
GOCACHE="/Users/zhimma/Library/Caches/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin" // GOOS 表示目标操作系统
GOPATH="/Users/zhimma/go" // GOPATH 表示当前工作目录
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go" // GOROOT 表示 Go 开发包的安装目录
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"

在 Go 1.8 版本之前,GOPATH 环境变量默认是空的。从 Go 1.8 版本开始,Go 开发包在安装完成后,将 GOPATH 赋予了一个默认的目录,参见下表。

平 台 GOPATH 默认值 举 例
Windows 平台 %USERPROFILE%/go C:\Users\用户名\go
Unix 平台 $HOME/go /home/用户名/go

使用GOPATH的工程结构

在GOPATH 指定的工作目录下,代码总是会保存在$GOPATH/src 目录下。在工程经过go build、go install或者go get 等指令后,会将产生的二进制可执行文件放在$GOPATH/bin目录下,生成的中间缓存文件会被保存在 $GOPATH/pkg下。

如果需要将整个源码添加到版本管理工具中时,只需要添加 $GOPATH/src 目录的源码即可。bin 和 pkg 目录的内容都可以由 src 目录生成。

设置和使用GOPATH

下面以Linux为例进行演示

设置当前目录为GOPATH

1
export GOPATH=`pwd`

建立GOPATH中的源码目录

使用下面指令创建GOPATH中的src 目录,在src目录下还有一个hello目录,该目录用于保存源码

1
mkdir -p src/hello

添加main.go源码文件

使用 Linux 编辑器将下面的源码保存为 main.go 并保存到 $GOPATH/src/hello 目录下。

1
2
3
4
5
package main
import "fmt"
func main(){
fmt.Println("hello")
}

编译源码并运行

此时我们已经设定了 GOPATH,因此在 Go 语言中可以通过 GOPATH 找到工程的位置。

在命令行中执行如下指令编译源码:

1
go install hello

编译完成的可执行文件会保存在 $GOPATH/bin 目录下。

在 bin 目录中执行 ./hello,命令行输出如下:

1
hello world

package(创建包)

包 是多个Go 源码的集合,是一种高级的代码复用方案,Go 语言默认为我们提供了很多包,如fmt、os、io包等。

包要求在同一个目录下的所有文件的第一行添加如下代码,以标记该文件归属的包:

1
package 包名

包的特性如下:

  • 一个目录下的同级文件归属一个包。
  • 包名可以与其目录不同名。
  • 包名为 main 的包为应用程序的入口包,编译源码没有 main 包时,将无法编译输出可执行的文件。

包中的标识符

如果想在一个包里引用另外一个包里的标识符(如类型、变量、常量等)时,必须首先将被引用的标识符导出,将要导出的标识符的首字母大写就可以让引用者可以访问这些标识符了。

import 导入包

要引用其他包的标识符,可以使用 import 关键字,导入的包名使用双引号包围,包名是从 GOPATH 开始计算的路径,使用/进行路径分隔。

默认导入的写法

导入有两种基本格式,即单行导入和多行导入,两种导入方法的导入代码效果是一致的

单行导入

单行导入格式如下:

1
2
import "包1"
import "包2"

2) 多行导入

当多行导入时,包名在 import 中的顺序不影响导入效果,格式如下:

1
2
3
4
5
import(
"包1"
"包2"

)

导入包后自定义引用的包名

在默认导入包的基础上,在导入包路径前添加标识符即可形成自定义引用包,格式如下:

1
customName "path/to/package"

其中,path/to/package 为要导入的包路径,customName 为自定义的包名。

1
2
3
4
5
6
7
8
package main
import (
renameLib "chapter08/importadd/mylib"
"fmt"
)
func main() {
fmt.Println(renameLib.Add(1, 2))
}

匿名导入包

如果只希望导入包,而不使用任何包内的结构和类型,也不调用包内的任何函数时,可以使用匿名导入包,格式如下:

1
2
3
import (
_ "path/to/package"
)

其中,path/to/package 表示要导入的包名,下画线_表示匿名导入包。

匿名导入的包与其他方式导入包一样会让导入包编译到可执行文件中,同时,导入包也会触发 init() 函数调用。

包在程序启动前的初始化入口:init

在某些需求的设计上需要在程序启动时统一调用程序引用到的所有包的初始化函数,如果需要通过开发者手动调用这些初始化函数,那么这个过程可能会发生错误或者遗漏。我们希望在被引用的包内部,由包的编写者获得代码启动的通知,在程序启动时做一些自己包内代码的初始化工作。

init() 函数的特性如下:

  • 每个源码可以使用 1 个 init() 函数。
  • init() 函数会在程序执行前(main() 函数执行前)被自动调用。
  • 调用顺序为 main() 中引用的包,以深度优先顺序初始化。

例如,假设有这样的包引用关系:main→A→B→C,那么这些包的 init() 函数调用顺序为:

1
C.init→B.init→A.init→main

说明:

  • 同一个包中的多个 init() 函数的调用顺序不可预期。
  • init() 函数不能被其他函数调用。