一、包的概念
- Go语言的包借助了目录树的组织形式,一般包的名称就是其源文件所在目录的名称。这就像在一个已经存在的包中生成一个子目录一样,在编写代码时,导入包需要做的仅仅只是提供这个要被嵌套的包的相对路径。
- Go语言都是以包为组织的,类似于其他语言中的库和模块。
代码中的层次结构,圈出的即为包名,其下可放多个源文件(注:此处IDE为VSCODE)
- 用 import 语法后跟包名来导入包,会优先搜寻GOROOT/src 目录中寻找包目录,如果没有找到,则会去 GOPATH/src
目录中继续寻找。
package main//包名,一般和源文件目录名保持一致
import ( //包的导入关键字
"fmt" //go语言内部包,在goroot/src下存在
"go测试程序/test01" //自定义包,在gopath/src存在
)
GOROOT下的fmt包
GOPATH下的test01包
- Go 语言中如果一个变量或函数的名称以大写字母开头就是可导出的,其他所有的名称不以大写字母开头的变量或函数都是这个包私有的。
- 被递归 import 的包的初始化顺序与 import 顺序相反,例如:导入顺序 main –> A –> B –> C,则初始化顺序为
C –> B –> A –> main - 一个包被其它多个包 import,但只能被初始化一次
- main 包总是被最后一个初始化,因为它总是依赖别的包
- 如果一个程序是 main 包的一部分,那么在 go install 则会生成一个二进制文件,在执行时则会调用 main 函数。如果一个程序除了 main 包外还是其他包的一部分,那么在使用 go install 命令时会创建包存档文件。(run main函数=install+执行main)
- 当某个包只需要初始化不需要使用时,包名前可加"_"。
二、Init函数
- 像 main 函数一样,init 函数在包被初始化时被 Go 调用。它不需要任何参数也不返回任何值。init 函数由 由 Go
隐式调用,因此你无法从任何地方引用它,但可以使用func init() 这样来调用它。 - **init 函数的主要作用是将在全局代码中无法初始化的全局变量初始化。例如,数组的初始化。
三、代码执行顺序
Go语言代码执行顺序为
- 初始化所有被导入的包
- 初始化被导入的包所有全局变量
- 被导入的包init函数调用
- Main函数执行
- 其中init函数调用顺序为
同一个go文件的init()调用顺序是从上到下的。
- 同一个package中不同文件是按文件名字符串比较“从小到大”顺序调用各文件中的init()函数。
- 不同的package,按照main包中"先import的先调用"的顺序调用其包中的init()
一个例子
main\hello.go
package main //包名,一般和源文件目录名保持一致
import ( //包的导入关键字
"fmt" //go语言内部包,在goroot/src下存在
"go测试程序/test01" //自定义包,当前路径为gopath/src
"go测试程序/test02"
)
func init() {
fmt.Println("init=======>main")
}
var testArg = test02.TestArg
func main() {
fmt.Println("hello world!")
fmt.Println("getValue", testArg)
fmt.Println(test01.WriteHelloWorld())
fmt.Println(test02.WriteHelloWorld())
}
test01\aaatest1.go
package test01
import (
"fmt"
)
var testArg01 = getArg()
func init() {
fmt.Println("init=======>aaatest1")
}
func getArg() string {
fmt.Println("globle init=======>aaatest1")
return "globle init=======>aaatest1"
}
test01\test01.go
package test01
import (
"fmt"
)
var testArg = "getTest01Arg"
func init() {
fmt.Println("init=======>test01 [1]")
}
func init() {
fmt.Println("init=======>test01 [2]")
}
//WriteHelloWorld test.
func WriteHelloWorld() string {
return "hello world from test01"
}
test02\test02.go
package test02
import (
"fmt"
)
var testArg = GetTestArg() //无法被外部包访问
//TestArg globlearg.
var TestArg = GetTestArg()
func init() {
fmt.Println("init=======>test02")
}
//GetTestArg test.
func GetTestArg() string {
fmt.Println("Globle Init from test02")
return "TestArgFromTest02"
}
//WriteHelloWorld test.
func WriteHelloWorld() string {
return "hello world from test02"
}
运行结果解释:
- 从main开始执行,首先在GOROOT/src下找到fmt包,导入
- 在GOPATH/src/下找到test01包,导入
- 因aaatest1文件名字典序在test01之前,因此先对aaatest1中的全局变量进行初始化操作,输出“globle init=======>aaatest1”
- 执行aaatest1.go中的init函数,输出“init=======>aaatest1”
- 顺序执行test01的init函数,输出
“init=======>test01 [1]”
“init=======>test01 [2]” - 在GOPATH/src/下找到test02包,导入
- 初始化test02.go的两个全局变量,输出两次“Globle Init from test02”
- 执行test02.go中的init函数,输出“init=======>test02”
- init main包,输出“init=======>main”
- 顺序执行main函数,得到结果
四、VSCode编译的坑
1.如果遇到“exportedxxx should have comment or be unexportedgo-lint”这样的提示性信息(不影响程序运行),则需要向变量或函数加注释,注释规则为“变量名(函数名)+注释+句号”,即
//WriteHelloWorld test.
func WriteHelloWorld() string {
return "hello world from test01"
}
2.如果发现在import加入包名后保存,VSCode自动删除了所加入包的代码,则需要查看该包的路径是否正确,若不正确VScode会自动删除包的引用。