Goinit函数介绍前言init函数介绍init

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

我们都知道 golang 的入口函数是 main 函数,在 main 函数中执行一些初始化工作。

今天我们就来学习 golang 中的另外一个特殊的函数 init(),这个函数的作用也是执行一些初始化操作。接下来我们就来深入学习一下 init() 函数的特性。

init 函数介绍

init 官方文档 中我们可以看到 go 对 init 函数有如下定义:

The init function

Finally, each source file can define its own niladic init function to set up whatever state is required. (Actually each file can have multiple init functions.) And finally means finally: init is called after all the variable declarations in the package have evaluated their initializers, and those are evaluated only after all the imported packages have been initialized.

Besides initializations that cannot be expressed as declarations, a common use of init functions is to verify or repair correctness of the program state before real execution begins.

从上述信息中,我们可以得知:

  1. 每个源文件都可以定义init() 函数,用来执行运行前一些准备工作;

  2. 每个文件可以包含多个 init() 函数;

  3. init() 函数初始化是在一个包中的所有全局变量初始化完成之后才开始运行;

init 函数特性

了解完 init() 函数功能之后,我们来看一看 init() 函数的一些特性。

存在多个 init 函数

Actually each file can have multiple init functions.

从这句话我们可以看出,不仅在同一个包中会有多个 init() 函数,在同一个源文件中也会存在多个 init() 函数。

生命周期

init is called after all the variable declarations in the package have evaluated their initializers, and those are evaluated only after all the imported packages have been initialized.

从上面我们可以得知,init() 函数是在一个包中所有全局变量都初始化完成之后,才开始运行。

这个特性非常有利于代码的组织。

我们的一个 package 中,肯定有很多源文件,我们可以把和源文件有关的 init() 函数写在对应文件中,方便理解、管理。

没有输入输出参数

each source file can define its own niladic init function to set up whatever state is required.

从上述介绍中我们可以看到 init() 函数是一个 niladic 函数。

a niladic function is a special type of function that takes no arguments and is evaluated immediately when it is encountered in a statement.

一个 niladic 函数就是一个没有输入输出的函数。

当我们在一个 init() 函数中添加参数时,编译器会提示我们如下错误信息:


Function init must have no arguments and no return values

复制代码

执行顺序

先说结论:

  1. init() 函数先于 main() 函数执行;

  2. 同一个文件中,存在多个 init() 函数时,从上向下执行;

  3. 多个文件按照字典顺序依次执行;

我们来试验一下,我们定义 4 个文件,分别为:Test9TestATestBHandler 方法,其中 main 方法在 Handler 中,我们运行一下看看结果:

// main 方法
func main() {
    fmt.Println("func main run")
}

// Test9 类 init 方法
func init() {
    fmt.Println("test9 init1 run")
}

// TestA 类 init 方法
func init() {
    fmt.Println("testA init1 run")
}

func init() {

    fmt.Println("testA init2 run")

}

// TestB 类 init 方法

func init() {

fmt.Println("testB init1 run")

}

复制代码

运行结果如下:


test9 init1 run

testA init1 run

testA init2 run

testB init1 run

func main run

复制代码

可以看到,和我们上述结论一致。

用作 side effect

有时,我们想要调用某个包中的 init() 方法,但是又不想要显示使用这个包,我们通常这样做,使用 _ 符号来重命名。


import _ "net/http/pprof"

复制代码

这个是 init() 函数的一个常见用法。

作用

现在我们来总结一下 init() 的作用:

  1. init() 目的是实现包级别的一些初始化操作;

  2. 初始化不能采用初始化表达式初始化的函数;

  3. 实现 sync.once 功能(单例模式);

  4. 运行前检查/修复状态;

参考文档