一、包

包是函数和数据的集合,将具有相同特性的函数和数据进行统一的管理,每个包都可以作为独立的单元维护,并提供给其他项目使用。

1.1、声明

Go源文件都需要在开头使用 package声明所在包,包名告知编译器哪些是包的源代码用于编译库文件,其次包名用于限制包内成员对外的可见性,最后包名用于在包外对公开成员的访问。

包名使用简短的小写字母,常与所在目录名保持一致,一个包中可以由多个 Go源文件,但必须使用相同包名

package main

1.2、导入

使用import导入包,import可以每行导入1个包,也可以使用括号同时导入多个包

import "fmt"
或者
import (
	"fmt"
	"mypackage"
)

包的导入方式

  • 绝对路径导入:在GOPATH目录查找包
  • 相对路径导入:在当前文件所在目录查找
  • 点导入:在调用点导入包中的成员时可以直接使用成员名称进行调用
  • 别名导入:为导入的包设置别名,避免不同路径的相同包名导入会冲突
  • 下划线导入:go不允许包导入却不使用,在某些情况下需要初始化包,使用下划线作为别名导入包时,包中的初始化函数(init函数)会执行
import "fmt"
import "./mypackage"
import . "fmt"
import f "fmt"
import _ "mypackage"

1.3、调用

绝对路径导入方式调用

import "fmt"

func main() {
    fmt.Println("hello")
}

点导入方式调用

import . "fmt"

func main() {
    Println("hello")
}

别名导入方式调用

import f "fmt"

func main() {
    f.Println("hello")
}

 

1.4、成员可见性

Go语言使用名称首字母大小写来判断对象(常量、变量、函数、类型、结构体、方法等)的访问权限,首字母大写标识包外可见(公开的),否则仅包内可访问(内部的)

var Name string = "alex" //public
var age int = 22 //private

 

1.5、main与init

main包用于声明告知编译器将包编译为二进制可执行文件,在 main包中的 main函数是程序的入口,无返回值,无参数。

init函数是初始化包时使用,无返回值,无参数

 

二、go module机制

从1.13版本之后,go module成为Go语言默认的版本管理工具

module的优势

  • 不需要设置GOPATH
  • 自动下载依赖管理
  • 版本控制
  • 不允许使用相对导入
  • replace机制

2.1、启用 go module

修改go的环境变量 GO111MODULE 以启用 go module,GO111MODULE有三个值 on,off,auto,默认值为auto;使用 go module管理依赖后会在项目根目录下生成两个文件go.mod和go.sum。

GO111MODULE="off"GOPATHvendorGO111MODULE="on"GOPATHvendorgo.modGO111MODULE="auto"$GOPATH/srcgo.mod
go env -w GO111MODULE="on"

 

2.2、初始化

创建一个新的项目时,先初始化

go mod init 项目名

    此时,会创建一个go.mod的文件在当前项目目录,在使用 go mod tidy、go build、go test、go list命令会自动将第三方依赖包写入到go.mod文件中同时下载第三方依赖包到 GOPATH/pkg/mod/cache目录,并在当前模块目录生成一个构建状态跟踪文件 go.sum,文件中记录当前 module所有的顶层和间接依赖,以及这些依赖的校验和。

go.mod文件大致如下

module hello

go 1.16

require (
	github.com/kr/fs v0.1.0 // indirect
	github.com/kr/pretty v0.2.1 // indirect
	github.com/pmezard/go-difflib v1.0.0 // indirect
	github.com/tools/godep v0.0.0-20180126220526-ce0bfadeb516 // indirect
	golang.org/x/tools v0.1.4 // indirect
)

其中

  • module定义包名
  • go 1.16 是go的版本
  • require 定义依赖包及版本
  • indirect 表示间接引用,版本号+时间戳+哈希值

除此之外,还有replace 替换定义的依赖包及版本,以及exclude。

require "test" v0.0.0
replace "test" => "../test"

 

2.3、常用命令

常用的命令如下

go mod init 初始化当前目录,在当前目录创建go.mod文件
go mod tidy 根据模块的实际使用情况在go.mod文件中添加或删除
go mod edit 编辑go.mod文件
go mod download 下载第三方模块到本地缓存(默认在$GOPATH/pkg/mod目录)
go mod graph 打印所有第三方模块
go mod verify 检验依赖
go mod why 解释为什么需要包或模块
go build 编译当前模块
go build ./... 编译当前目录下所有模块
go list -m -json all 显示所有模块信息
go list std 显示所有标准包
go doc PACKAGENAME 查看 PACKAGENAME 包的帮助信息

 

三、go module 导入包

3.1、导入同一个项目的包

在一个项目下可以定义多个包

目录结构

├── cmdb.go
├── go.mod
└── user
    └── user.go

创建cmdb目录,并初始化

go mod init cmdb

user/user.go

package user

import "fmt"

func Add() {
	fmt.Println("add user")
}

cmdb.go

调用user包的Add函数

package main

import (
	"cmdb/user"
	"fmt"
)

func main() {
	fmt.Println("hello")
	user.Add()
}

运行

ericdeMBP:cmdb eric$ go run .
hello
add user

 

3.2、导入不在同一个项目下的包

目录结构

├── cmdb
│   ├── cmdb.go
│   └── go.mod
└── user
    ├── go.mod
    └── user.go

cmdb/cmdb.go

package main

import (
	"user"
	"fmt"
)

func main() {
	fmt.Println("hello")
	user.Add()
}

因为user包与cmdb不在同一个项目内,并且也没有发布在代码仓库中,所以如果要导入包需要使用replace

cmdb/go.mod

module cmdb

go 1.16

require "user" v0.0.0
replace "user" => "../user"

运行

ericdeMBP:cmdb eric$ go run .
hello
add user

 

 

go 标准包:https://golang.google.cn/pkg/

go 第三方包:https://pkg.go.dev/

参考文档:《手撕go语言》