Go中包、Init函数与执行顺序

一、包的概念

  1. Go语言的包借助了目录树的组织形式,一般包的名称就是其源文件所在目录的名称。这就像在一个已经存在的包中生成一个子目录一样,在编写代码时,导入包需要做的仅仅只是提供这个要被嵌套的包的相对路径。
  2. Go语言都是以包为组织的,类似于其他语言中的库和模块。
    代码中的层次结构,圈出的即为包名,其下可放多个源文件(注:此处IDE为VSCODE)

在这里插入图片描述

  1. 用 import 语法后跟包名来导入包,会优先搜寻GOROOT/src 目录中寻找包目录,如果没有找到,则会去 GOPATH/src
    目录中继续寻找。
package main//包名,一般和源文件目录名保持一致

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

GOROOT下的fmt包
**加粗样式**
GOPATH下的test01包
在这里插入图片描述

  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函数执行
  5. 其中init函数调用顺序为

同一个go文件的init()调用顺序是从上到下的。

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

在这里插入图片描述

运行结果解释:

  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会自动删除包的引用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值