什么是包?
就是一种命名空间的体现,或者说就是一个放多个go文件的目录。
不同包之间是隔离的,需要 import 才能访问其它包下的对外成员。
同一个包下,作用域是相同的,如下:
main.go 和 calc.go 都在同一个目录下,并且都属于 main 包,所以你会看到,main.go 中可以直接调用 calc.go 中的 add() 函数
假如它们不在同一个包下呢,main.go 又该如何调用 add() ?
注意标红的地方:
- 函数首字母为什么要大写? 一种约定写法,表示对外开放
- 引入包,是按模块名称为准的,因为calc包在 helloWorld 模块下。所以是 import “helloWorld/calc”
- 同一个包下,使用包的成员,就是直接用就行,不用像上面的 calc.Add(1, 2),并且不需要首字母大写。
什么是模块,和包有什么关系?
模块可以理解为一个项目,这个项目可以有多个包组成、
直接看例子:
假设我们有一个聊天服务 imSvr 模块
imSvr/
- go.mod
module github.com/junwind/imSvr // 模块名称
go 1.18
- roomSvr // 包
- room.go
package roomSvr
...
- msgSvr // 包
- msg.go
package msgSvr
...
- main.go // 服务入口
package main
import (
"imSvr/roomSvr"
"imSvr/msgSvr"
)
func main() {
if roomId == nil {
roomSvr.CreateRoom()
} else {
roomSvr.JoinRoom(roomId)
if sendMsg != nil {
msgSvr.Push(roomId, sendMsg)
}
}
}
如何定义一个包?
直接在文件开头写上 package 包名
一般一个目录就是一个包,包名称一般和目录名称一致,它下面的直属文件都属于这个包(子目录也可能是另一个包),比如:
pveSvr
pk.go
package pveSvr
...
worldBossBattle
battle.go
package worldBossBattle
...
包名不能包含 -
符号
main包是程序的入口包,这种包编译后会得到一个可执行的文件。
如何访问包的成员呢?
1、在同一个包下,因为都在一个作用域下,直接访问成员本身就行。
2、不同包下,需要 import,使用时,还需要带上 包名.成员,比如 fmt.Println()
3、想要让包下的成员能被外部访问,该成员名称的首字母必须大写才行。
来看一个例子:
package demo
type Student struct { // Student结构体能不能在外部访问呢?
Name string // Name字段能不能在外部访问呢?
class string // class字段能不能在外部访问呢?
}
大家手动试一下吧。
怎么引用一个包
import importname "module/package"
importname 可以理解为包的别名,默认就是包名,一般不写
为什么要有这个包别名机制呢?
想一想,假如两个包名称刚好相同呢,那么我们使用起来岂不是就冲突了。
import f "fmt"
import (
test1 test
test2 test
)
注意:go中,禁止循环引用包,比如 A包中用B包,B包中用A包。
如何匿名引用一个包,它有什么用?
import _ "github.com/go-sql-driver/mysql"
作用就是为了执行这个包中的 init() 函数。
注意:被匿名引入的包中的init()函数会被执行,而且仅执行一次。
init() 函数是干啥的?
先看代码
func init() { // 没有参数,没有返回值
fmt.Println("init001")
}
func init() {
fmt.Println("init002")
}
func main() {
fmt.Println("main")
}
执行后,会发现,init函数先执行,也就是说在程序真正执行之前,我们可以在init中做一些初始化动作。
引入的包中,init() 的执行顺序
包里面有没有比 init() 更早的声明呢?
有,在全局作用域(函数外)下的声明的变量,常量,函数等等,都是可以直接在init() 中调用的。大家可以自行测试一下。
由此我们会得知,包的执行顺序了:
1、 处理全局的声明
2、执行引入其它包中的init,执行当前包下的init
3、执行当前包下的main