首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01|前世今生:你不得不了解的Go的历史和现状
02|拒绝“Hello and Bye”:Go语言的设计哲学是怎么一回事?
03|配好环境:选择一种最适合你的Go安装方法
04|初窥门径:一个Go程序的结构是怎样的?
05|标准先行:Go项目的布局标准是什么?
06|构建模式:Go是怎么解决包依赖管理问题的?
07|构建模式:Go Module的6类常规操作
08|入口函数与包初始化:搞清Go程序的执行次序
09|即学即练:构建一个Web服务就是这么简单
10|变量声明:静态语言有别于动态语言的重要特征
11|代码块与作用域:如何保证变量不会被遮蔽?
12|基本数据类型:Go原生支持的数值类型有哪些?
13|基本数据类型:为什么Go要原生支持字符串类型?
14|常量:Go在“常量”设计上的创新有哪些?
15|同构复合类型:从定长数组到变长切片
16|复合数据类型:原生map类型的实现机制是怎样的?
17|复合数据类型:用结构体建立对真实世界的抽象
18|控制结构:if的“快乐路径”原则
19|控制结构:Go的for循环,仅此一种
20|控制结构:Go中的switch语句有哪些变化?
21|函数:请叫我“一等公民”
22|函数:怎么结合多返回值进行错误处理?
23|函数:怎么让函数更简洁健壮?
24|方法:理解“方法”的本质
25|方法:方法集合与如何选择receiver类型?
26|方法:如何用类型嵌入模拟实现“继承”?
27|即学即练:跟踪函数调用链,理解代码更直观
28|接口:接口即契约
29|接口:为什么nil接口不等于nil?
30|接口:Go中最强大的魔法
31|并发:Go的并发方案实现方案是怎样的?
32|并发:聊聊Goroutine调度器的原理
33|并发:小channel中蕴含大智慧
34|并发:如何使用共享变量?
35|即学即练:如何实现一个轻量级线程池?
36|打稳根基:怎么实现一个TCP服务器?(上)
37|代码操练:怎么实现一个TCP服务器?(中)
38|成果优化:怎么实现一个TCP服务器?(下)
39 | 驯服泛型:了解类型参数
40|驯服泛型:定义泛型约束
41 | 驯服泛型:明确使用时机
当前位置:
首页>>
技术小册>>
Go语言入门实战经典
小册名称:Go语言入门实战经典
### 第八章 入口函数与包初始化:搞清Go程序的执行次序 在Go语言的世界中,程序的执行流程与传统编程语言有着显著的不同,尤其是在程序启动和包(package)的初始化方面。理解这些概念及其执行顺序对于编写高效、可维护的Go程序至关重要。本章将深入探讨Go程序的入口函数(即`main`函数)以及包(package)的初始化机制,帮助读者清晰地掌握Go程序的执行次序。 #### 8.1 Go程序的入口:`main`函数 在Go语言中,每个可执行程序都必须包含一个名为`main`的包,并且该包内必须有一个无参数、无返回值的`main`函数。这个函数是程序的入口点,即程序开始执行的地方。`main`函数通常位于一个名为`main.go`的文件中(文件名并非强制要求,但遵循此约定有助于保持项目的组织性)。 ```go package main import ( "fmt" ) func main() { fmt.Println("Hello, Go!") } ``` 上述代码展示了最简单的Go程序结构,它打印出“Hello, Go!”到标准输出。这个`main`函数就是整个程序的起点。 #### 8.2 包的初始化 在Go程序中,包(package)不仅仅是代码组织的一种方式,它们还承载着初始化逻辑。每个包在被首次导入时,其初始化代码(包括变量初始化语句和`init`函数)将按照特定的顺序执行。这种机制允许包在全局范围内设置必要的状态,为程序的其余部分提供所需的资源或配置。 ##### 8.2.1 变量初始化 Go语言支持在包级别定义变量,并在包初始化时自动执行其初始化表达式。这些变量可以是常量、变量或者通过函数返回的值。 ```go package mypackage var MyVar = "Initialized in mypackage" func init() { // 可以在这里执行更复杂的初始化逻辑 } ``` 在上面的例子中,`MyVar`在`mypackage`包被导入时会被初始化。 ##### 8.2.2 `init`函数 除了变量初始化外,Go还允许在包中定义`init`函数。每个包可以包含多个`init`函数,它们没有参数也没有返回值。这些`init`函数将在包被导入时自动调用,且调用顺序不保证(但同一包内的`init`函数会按照它们在源代码中出现的顺序执行)。 ```go package mypackage import "fmt" var Count int func init() { fmt.Println("First init in mypackage") Count = 1 } func init() { fmt.Println("Second init in mypackage, Count:", Count) Count++ } ``` 在这个例子中,当`mypackage`被导入时,会依次打印出“First init in mypackage”和“Second init in mypackage, Count: 1”,展示了`init`函数的调用顺序及其影响。 #### 8.3 程序的执行次序 现在,让我们综合以上知识点,探讨Go程序的整体执行次序: 1. **包初始化**: - 当程序启动时,首先会初始化所有被导入的包(包括标准库包和自定义包)。 - 对于每个包,首先执行包级别的变量初始化(按照它们在源代码中出现的顺序)。 - 然后,依次执行该包内的所有`init`函数(同样按照它们在源代码中出现的顺序)。 - 如果有多个包被导入,且这些包之间存在依赖关系,则首先初始化依赖包。 2. **`main`函数执行**: - 在所有必要的包都完成初始化之后,才会开始执行`main`包中的`main`函数。 - `main`函数是程序的唯一入口点,它的执行标志着程序的主要逻辑开始运行。 #### 8.4 示例分析 为了更直观地理解上述执行次序,考虑以下示例: ```go // main.go package main import ( "fmt" "example/mypackage" ) func main() { fmt.Println("In main function") fmt.Println("mypackage.Count:", mypackage.Count) } // mypackage/mypackage.go package mypackage import "fmt" var Count int func init() { fmt.Println("Initializing mypackage, setting Count to 1") Count = 1 } func init() { fmt.Println("Further initializing mypackage, incrementing Count") Count++ } ``` 当运行上述程序时,输出将类似于: ``` Initializing mypackage, setting Count to 1 Further initializing mypackage, incrementing Count In main function mypackage.Count: 2 ``` 这个输出清晰地展示了包初始化(包括变量初始化和`init`函数执行)先于`main`函数执行的顺序,以及`init`函数在同一包内的调用顺序。 #### 8.5 总结 理解Go程序的执行次序,特别是包的初始化和`main`函数的执行,对于编写健壮、可预测的Go程序至关重要。通过本章的学习,我们了解到: - Go程序从`main`包的`main`函数开始执行。 - 在`main`函数执行之前,所有被导入的包都会按照依赖关系进行初始化,包括执行包级别的变量初始化和`init`函数。 - 同一包内的`init`函数按照它们在源代码中出现的顺序执行,但不同包之间的`init`函数执行顺序则取决于包的导入顺序和依赖关系。 掌握这些概念,将帮助你在编写Go程序时更好地组织代码,管理资源,以及避免潜在的初始化问题。
上一篇:
07|构建模式:Go Module的6类常规操作
下一篇:
09|即学即练:构建一个Web服务就是这么简单
该分类下的相关小册推荐:
深入浅出Go语言核心编程(二)
Go开发权威指南(下)
Go-Web编程实战
Go 组件设计与实现
企业级Go应用开发从零开始
Go Web编程(下)
Golang修炼指南
深入浅出Go语言核心编程(八)
深入浅出Go语言核心编程(一)
从零写一个基于go语言的Web框架
深入浅出Go语言核心编程(三)
go编程权威指南(三)