包(package)是Go语言组织代码的基本单位,每个Go文件都属于一个包。模块(module)是Go 1.11引入的依赖管理工具,通过go.mod文件定义模块路径和依赖,取代了旧的GOPATH模式。掌握包和模块是构建可复用、可维护Go程序的基础。
GOPATH。
每个Go文件开头都通过package语句声明所属包。同一目录下的所有.go文件必须属于同一个包。
// 文件:mymath/mymath.go
package mymath
// 首字母大写,表示公开函数
func Add(a, b int) int {
return a + b
}
// 首字母小写,包内私有
func subtract(a, b int) int {
return a - b
}
// 文件:main.go
package main
import (
"fmt"
"mymath" // 导入自定义包(假设已模块化)
)
func main() {
sum := mymath.Add(5, 3)
fmt.Println(sum) // 8
// subtract 无法访问,因为未导出
}
包名通常使用小写单词,与目录名保持一致。
使用import语句导入包,可单独导入、批量导入或设置别名。
import "fmt"
import "os"
// 批量导入
import (
"fmt"
"os"
)
// 别名
import (
myfmt "fmt" // 使用 myfmt.Println
_ "image/png" // 匿名导入,仅执行init函数
. "math" // 直接使用包内函数(不推荐)
)
匿名导入(_)通常用于注册驱动或执行包的初始化函数。
从Go 1.11开始,官方推荐使用模块管理依赖。模块是一个包含go.mod文件的目录,该文件定义了模块路径和所需的依赖版本。
使用go mod init 模块路径创建go.mod文件。
mkdir myproject
cd myproject
go mod init github.com/username/myproject
生成的go.mod内容:
module github.com/username/myproject
go 1.22
在代码中导入外部包,运行go mod tidy会自动下载并记录依赖。
// main.go
package main
import "github.com/gorilla/mux"
func main() {
r := mux.NewRouter()
// ...
}
go mod tidy # 自动添加依赖
更新后的go.mod:
module github.com/username/myproject
go 1.22
require github.com/gorilla/mux v1.8.1
同时生成go.sum文件,记录依赖的哈希值,确保完整性。
Go模块遵循语义版本控制(SemVer)。对于v2+的主版本,模块路径必须包含版本路径,例如:
require github.com/example/module/v2 v2.0.0
导入时也需要带路径:
import "github.com/example/module/v2/pkg"
init函数在包被导入时自动执行,用于初始化状态,没有参数和返回值。一个包可以有多个init,按文件顺序执行。
package database
var dbConn *sql.DB
func init() {
// 初始化数据库连接
dbConn = connect()
}
可见性规则:Go通过标识符首字母大小写控制可见性。
Add):对外公开,其他包可访问。subtract):包内私有,仅同包内可访问。目录名为internal的包只能被其父目录及子目录下的包导入,不能被外部项目导入。用于强制封装内部实现。
myproject/
├── go.mod
├── internal/
│ └── auth/ # 只能被 myproject 内部导入
│ └── token.go
└── pkg/
└── public/ # 可以被外部导入
| 命令 | 说明 |
|---|---|
go mod init <module> | 初始化新模块 |
go mod tidy | 添加缺失的依赖,删除未使用的依赖 |
go get <package>[@version] | 添加或升级依赖 |
go mod download | 下载依赖到本地缓存 |
go mod vendor | 将依赖复制到vendor目录 |
go list -m all | 查看当前模块的所有依赖 |
设置GOPROXY环境变量可以加速依赖下载(国内推荐https://goproxy.cn,direct)。
go env -w GOPROXY=https://goproxy.cn,direct
如果依赖库有问题,可以在go.mod中使用replace指令替换为本地路径或其他版本。
replace github.com/old/pkg => ../local/pkg
stringutils/
├── go.mod
├── reverse/
│ └── reverse.go
└── main.go
// stringutils/go.mod
module github.com/example/stringutils
go 1.22
// stringutils/reverse/reverse.go
package reverse
func String(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
// stringutils/main.go
package main
import (
"fmt"
"github.com/example/stringutils/reverse"
)
func main() {
original := "Hello, 世界"
reversed := reverse.String(original)
fmt.Printf("原字符串: %s\n反转后: %s\n", original, reversed)
}
GOPATH的限制,轻松管理依赖版本。掌握包的可见性、init函数、内部包等特性,有助于构建结构清晰、可维护的Go项目。下一章将介绍Go语言并发编程的核心——goroutine与channel。