第 10 章 包
包(package) 用于组织 Go 源代码,提供了更好的可重用性与可读性。Go 语言有超过 100 个的标准包,可以用 go list std | wc -l
命令查看标准包的具体数目,标准库为大多数的程序提供了必要的基础组件。
10.1 main 包
首先,我们先来看看 main
包,该包中包含一个 main()
函数,该函数是程序运行的入口。
package packagename
代码指定了某一源文件属于某一个包。它应该放在每一个源文件的第一行。例如我们 Go 的第一个程序。
// hello go
package main
import "fmt"
func main() {
fmt.Println("《Go语言极简一本通》")
}
package main
这一行指定该文件属于 main
包。import "fmt"
语句用于导入一个已存在的名为 fmt
的包。
10.2 创建包
下面我们创建自定义的 book
包,其中,属于某一个包的源文件都应该放置于一个单独命名的文件夹里,按照 Go 的惯例,应该用包名命名该文件夹。所以应当先创建一个 book
文件夹,位于该目录下创建一个 book.go
源文件,里面实现我们自定义的数学加法函数。请注意函数名的首字母要大写。
// book.go
package book
func ShowBookInfo(bookName, authorName string) (string, error) {
if bookName == "" {
return "", errors.New("图书名称为空")
}
if authorName == "" {
return "", errors.New("作者名称为空")
}
return bookName ",作者:" authorName, nil
}
Tips:
- 导出名字(Exported Names)
- 我们将
book
包中的函数ShowBookInfo
首字母大写。在 Go 中这具有特殊意义。在 Go 中,任何以大写字母开头的变量或者函数都是被导出的名字。其它包只能访问被导出的函数和变量。在这里,我们需要在main
包中访问ShowBookInfo
函数,因此会将它们的首字母大写。 - 如果在
book.go
中将函数名从ShowBookInfo
变为showBookInfo
,并且在main.go
中调用book.showBookInfo
函数,则该程序编译不通过。因为如果想在包外访问一个函数,它应该首字母大写。
- 我们将
10.3 导入包
使用包之前我们需要导入包,在 GoLand 中会帮你自动导入所需要的包。导入包的语法为 import path
,其中 path
可以是相对于工作区文件夹的相对路径,也可以是绝对路径。
package main
import (
"fmt"
"book"
)
func main() {
bookName := "《Go语言极简一本通》"
author := "欢喜"
bookInfo, _ := book.ShowBookInfo(bookName, author)
fmt.Println("bookInfo = ", bookInfo)
}
导入包可以单行导入也可以多行导入,像上面的程序代码就是多行导入的例子,一般我们也建议使用多行导入,当然你也可以使用单行导入:
代码语言:go复制import "fmt"
import "book"
10.4 使用别名
如果我们导入了两个具有同一包名的包时会产生冲突,这时我们可以为其中一个包定义别名:
代码语言:go复制import (
"crypto/rand"
mrand "math/rand" // 将名称替换为 mrand 避免冲突
)
当然,我们也可以使用别名代替名字很长的包名。
10.5 使用点操作
导入一个包后,如果要使用该包中的函数,都要使用 包名.方法名
语法进行调用,对于一些使用高频的包,例如 fmt
包,每次调用打印函数时都要使用 fmt.Println()
进行调用,很不方便。我们可以在导入包的时,使用 import . package_path
语法。从此,我们打印再也不用加 fmt
了。
import . "fmt"
func main() {
Println("hello, world")
}
但这种用法,会有一定的隐患,就是导入的包里可能有函数,会和我们自己的函数发生冲突。
10.6 包的初始化
每个包都允许有一个或多个 init
函数, init
函数不应该有任何返回值类型和参数,在代码中也不能显式调用它,当这个包被导入时,就会执行这个包的 init
函数,做初始化任务, init
函数优先于 main
函数执行。该函数形式如下:
func init() {
}
包的初始化顺序:首先初始化 包级别(Package Level) 的变量,紧接着调用 init
函数。包可以有多个 init
函数(在一个文件或分布于多个文件中),它们按照编译器解析它们的顺序进行调用。如果一个包导入了另一个包,会先初始化被导入的包。尽管一个包可能会被导入多次,但是它只会被初始化一次。
10.7 包的匿名导入
之前说过,导入一个没有使用的包编译会报错。但有时候我们只是想执行包里的 init
函数来执行一些初始化任务的话应该怎么办呢?
我们可以使用匿名导入的方法,使用 空白标识符(Blank Identifier) :
代码语言:go复制import _ "fmt"
由于导入时会执行该包里的 init
函数,所以编译仍会将此包编译到可执行文件中。