【Go语言学习】包、Init函数与执行顺序

一、包的概念

  1. Go语言的包借助了目录树的组织形式,一般包的名称就是其源文件所在目录的名称。这就像在一个已经存在的包中生成一个子目录一样,在编写代码时,导入包需要做的仅仅只是提供这个要被嵌套的包的相对路径。
  2. Go语言都是以包为组织的,类似于其他语言中的库和模块。
    代码中的层次结构,圈出的即为包名,其下可放多个源文件(注:此处IDE为VSCODE)
    【Go语言学习】包、Init函数与执行顺序_第1张图片
  3. 用 import 语法后跟包名来导入包,会优先搜寻GOROOT/src 目录中寻找包目录,如果没有找到,则会去 GOPATH/src 目录中继续寻找。
package main//包名,一般和源文件目录名保持一致

import ( 	//包的导入关键字
	"fmt"	//go语言内部包,在goroot/src下存在
	"go测试程序/test01" //自定义包,在gopath/src存在
)

GOROOT下的fmt包
【Go语言学习】包、Init函数与执行顺序_第2张图片
GOPATH下的test01包
【Go语言学习】包、Init函数与执行顺序_第3张图片

  1. Go 语言中如果一个变量或函数的名称以大写字母开头就是可导出的,其他所有的名称不以大写字母开头的变量或函数都是这个包私有的。
  2. 被递归 import 的包的初始化顺序与 import 顺序相反,例如:导入顺序 main –> A –> B –> C,则初始化顺序为 C –> B –> A –> main
  3. 一个包被其它多个包 import,但只能被初始化一次
  4. main 包总是被最后一个初始化,因为它总是依赖别的包
  5. 如果一个程序是 main 包的一部分,那么在 go install 则会生成一个二进制文件,在执行时则会调用 main 函数。如果一个程序除了 main 包外还是其他包的一部分,那么在使用 go install 命令时会创建包存档文件。(run main函数=install+执行main)
  6. 当某个包只需要初始化不需要使用时,包名前可加"_"。

二、Init函数

  1. 像 main 函数一样,init 函数在包被初始化时被 Go 调用。它不需要任何参数也不返回任何值。init 函数由 由 Go 隐式调用,因此你无法从任何地方引用它,但可以使用func init() 这样来调用它。
  2. **init 函数的主要作用是将在全局代码中无法初始化的全局变量初始化。例如,数组的初始化。

三、代码执行顺序

Go语言代码执行顺序为

  1. 初始化所有被导入的包
  2. 初始化被导入的包所有全局变量
  3. 被导入的包init函数调用
  4. Main函数执行

其中init函数调用顺序为

  1. 同一个go文件的init()调用顺序是从上到下的。
  2. 同一个package中不同文件是按文件名字符串比较“从小到大”顺序调用各文件中的init()函数。
  3. 不同的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"
}


【Go语言学习】包、Init函数与执行顺序_第4张图片
运行结果解释:
1.从main开始执行,首先在GOROOT/src下找到fmt包,导入
2.在GOPATH/src/下找到test01包,导入
3.因aaatest1文件名字典序在test01之前,因此先对aaatest1中的全局变量进行初始化操作,输出“globle init=======>aaatest1”
4.执行aaatest1.go中的init函数,输出“init=======>aaatest1”
5.顺序执行test01的init函数,输出
“init=======>test01 [1]”
“init=======>test01 [2]”
6.在GOPATH/src/下找到test02包,导入
7.初始化test02.go的两个全局变量,输出两次“Globle Init from test02”
8.执行test02.go中的init函数,输出“init=======>test02”
9.init main包,输出“init=======>main”
10.顺序执行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会自动删除包的引用。

你可能感兴趣的:(GO入门)