go 语言中 init() 函数是什么时候执行的?

news2025/1/13 3:36:29

文章目录

  • 一、init() 函数什么时候执行?
  • 二、init() 函数特点
  • 三、代码执行顺序
  • 四、多个 init() 函数执行顺序
    • 1、一个源文件中多个 init() 函数
    • 2、一个包中多个 init() 函数
    • 3、多个包中多个 init() 函数(不存在依赖)
    • 4、多个包中多个 init() 函数(存在依赖)
    • 5、一个包会被多个包同时导入,它只会被导入一次,init() 函数只执行一次
  • 五、小结

一、init() 函数什么时候执行?

  在Go语言中,init 函数是一个特殊的函数,它在包被导入时自动执行,主要用于完成程序的初始化工作。例如初始化包级别的变量,或者执行包初始化时仅需执行一次的设置,如初始化资源、初始化数据库连接、载入本地配置文件、预先计算一些值、注册等。

  在 golang 中, init() 函数是在main() 函数之前执行的。

package main

func init() {
	println("init function called")
}

func main() {
	println("main function called")
}

运行结果:

在这里插入图片描述

二、init() 函数特点

  • init() 函数是可选的,源文件可以没有 init() 函数。
  • init() 函数没有入参和返回值,自动执行,不能被其他函数调用。(与 main 函数一样)
  • 同一个包,甚至是同一个源文件可以有多个 init() 函数。
  • 不管包被导入多少次,包内的 init 函数只会执行一次

三、代码执行顺序

  Go语言代码执行顺序为:import –> const –> var –>init()–>main()

1、初始化所有被导入的包;
2、初始化被导入的包所有全局常量、变量;
3、执行被导入的包 init() 函数;
4、层层递出最后执行 main() 函数。

在这里插入图片描述

需要注意的点:

  • 程序初始化的顺序是,从被导入的最深层包开始进行初始化,层层递出最后到 main 包;
  • Go的依赖分析器会尽可能并行地初始化包;
  • 程序的初始化和执行都起始于 main 包。如果 main 包还导入了其它的包,那么就会在编译时将它们依次导入;
  • 有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到 fmt 包,但它只会被导入一次,因为没有必要导入多次);
  • 不同包的init函数按照包导入的依赖关系决定执行顺序。
  • 在编码时避免依赖 init() 函数的执行顺序。虽然 init() 顺序是明确的,但代码可以更改,init() 函数之间的关系可能会使代码变得脆弱和容易出错,因此在编码时避免依赖 init() 函数的执行顺序。
// a 包
// a.go
package a

import _ "myGoProject/init_demo/b"

var a = func() int {
	println("init int a")
	return 'a'
}()

func init() {
	println("a init function called")
}


// b 包
// b.go
package b

import _ "myGoProject/init_demo/c"

var b = func() int {
	println("init int b")
	return 'b'
}()

func init() {
	println("b init function called")
}


// c 包
// c.go
package c

var c = func() int {
	println("init int c")
	return 'c'
}()

func init() {
	println("c init function called")
}


// main 包
// main.go
package main

import (
	_ "myGoProject/init_demo/a"
)

var m = func() int {
	println("init int m")
	return 1
}()

func init() {
	println("init function called")
}

func main() {
	println("main function called")
}

运行结果:

在这里插入图片描述

  结论:包级变量是在 init 函数之前完成初始化的。

四、多个 init() 函数执行顺序

  通过上面的内容我们可以知道,一个源文件可以有多个 init 函数,一个包也可以有多个 init 函数。 那么不同包、不同源文件中的多个 init 函数是按照什么顺序执行的呢?下面,我们通过一个个的小 demo 去分析。

1、一个源文件中多个 init() 函数

  main.go 源文件有三个 init() 函数。

package main

func init() {
	println("a init function called")
}

func init() {
	println("b init function called")
}

func init() {
	println("c init function called")
}

func main() {
	println("main function called")
}

运行结果:

在这里插入图片描述

  结论:一个源文件中多个 init() 函数的执行顺序是根据定义顺序确定,从上到下

  验证:把 3个 init() 函数的定义顺序改为 b、a、c。运行结果如下,执行顺序为 b、a、c。结论正确。

package main

func init() {
	println("b init function called")
}

func init() {
	println("a init function called")
}

func init() {
	println("c init function called")
}

func main() {
	println("main function called")
}

在这里插入图片描述

2、一个包中多个 init() 函数

  main 包有4个源文件 a.go、b.go、c.go、main.go。

在这里插入图片描述

// a.go
package main

func init() {
	println("a init function called")
}

// b.go
package main

func init() {
	println("b init function called")
}

// c.go
package main

func init() {
	println("c init function called")
}

// main.go
package main

func init() {
	println("init function called")
}

func main() {
	println("main function called")
}

运行结果:

在这里插入图片描述
  结论:一个包中多个 init() 函数的执行顺序是根据文件名的字典序确定,从小到大

  验证:把 a.go 文件名改为 d.go,内容不变。运行结果如下,先执行 b.go、c.go 的 init() 函数,然后才执行 d.go 的init() 函数。结论正确。

在这里插入图片描述
在这里插入图片描述

3、多个包中多个 init() 函数(不存在依赖)

  有a、b、c、main 4个包,每个包有1个同名源文件 a.go、b.go、c.go、main.go。

在这里插入图片描述

// a 包
// a.go
package a

func init() {
	println("a init function called")
}

// b 包
// b.go
package b

func init() {
	println("b init function called")
}

// c 包
// c.go
package c

func init() {
	println("c init function called")
}

// main 包
// main.go
package main

import (
	_ "myGoProject/init_demo/a"
	_ "myGoProject/init_demo/b"
	_ "myGoProject/init_demo/c"
)

func init() {
	println("init function called")
}

func main() {
	println("main function called")
}

运行结果:

在这里插入图片描述
  结论:多个包中多个 init() 函数,包不相互依赖,多个 init() 函数的执行顺序是根据 main 包中导入顺序确定,从上到下,最后再执行 main 包的 init 函数。

  验证:把导入顺序改为b、c、a,其他代码不变。运行结果如下,先执行 b、c 包的 init() 函数,然后才执行 a 包的init() 函数,最后执行 main 包的 init 函数。结论正确。

import (
	_ "myGoProject/init_demo/b"
	_ "myGoProject/init_demo/c"
	_ "myGoProject/init_demo/a"
)

在这里插入图片描述

4、多个包中多个 init() 函数(存在依赖)

  有a、b、c、main 4 个包,每个包有1个同名源文件 a.go、b.go、c.go、main.go,main 包依赖 a 包,a 包依赖 b 包,b 包依赖 c 包,依赖关系为 main > a > b > c。

在这里插入图片描述

// a 包
// a.go
package a

import _ "myGoProject/init_demo/b"

func init() {
	println("a init function called")
}

// b 包
// b.go
package b

import _ "myGoProject/init_demo/c"

func init() {
	println("b init function called")
}

// c 包
// c.go
package c

func init() {
	println("c init function called")
}

// main 包
// main.go
package main

import (
	_ "myGoProject/init_demo/a"
)

func init() {
	println("init function called")
}

func main() {
	println("main function called")
}

运行结果:

在这里插入图片描述

  结论:多个包中多个 init() 函数,包存在依赖,多个 init() 函数的执行顺序是根据包导入的依赖关系确定,从里到外,执行顺序为最后被依赖的包(最深层的包:c 包)最先被初始化,如依赖关系 main > a > b > c,则 init 函数执行顺序为 c > b > a > main。

  验证:把依赖关系改为 main > b > a > c,其他代码不变。运行结果如下,c 包的 init() 函数先执行,接着执行 a 包的init() 函数,然后执行 b 包的 init 函数,最后执行 main 包的 init 函数。结论正确。

// a 包
// a.go
package a

import _ "myGoProject/init_demo/c"

func init() {
	println("a init function called")
}

// b 包
// b.go
package b

import _ "myGoProject/init_demo/a"

func init() {
	println("b init function called")
}

// c 包
// c.go
package c

func init() {
	println("c init function called")
}

// main 包
// main.go
package main

import (
	_ "myGoProject/init_demo/b"
)

func init() {
	println("init function called")
}

func main() {
	println("main function called")
}

在这里插入图片描述

5、一个包会被多个包同时导入,它只会被导入一次,init() 函数只执行一次

  main 包依赖 a、c 包,a 包依赖 b、c 包,b 包依赖 c 包,c 包被 main、a、b 包依赖。

// a 包
// a.go
package a

import (
	_ "myGoProject/init_demo/b"
	_ "myGoProject/init_demo/c"
)

func init() {
	println("a init function called")
}

// b 包
// b.go
package b

import _ "myGoProject/init_demo/c"

func init() {
	println("b init function called")
}

// c 包
// c.go
package c

func init() {
	println("c init function called")
}

// main 包
// main.go
package main

import (
	_ "myGoProject/init_demo/a"
	_ "myGoProject/init_demo/c"
)

func init() {
	println("init function called")
}

func main() {
	println("main function called")
}


运行结果:

在这里插入图片描述

五、小结

  在 golang 中, init() 函数是在main() 函数之前执行的。全局常量、变量是在 init 函数之前初始化的。Go语言代码执行顺序为:import –> const –> var –>init()–>main()。

多个init() 函数执行顺序:
  1、一个源文件中多个 init() 函数的执行顺序是根据定义顺序确定,从上到下
  2、 一个包中多个 init() 函数的执行顺序是根据文件名的字典序确定,从小到大
  3、多个包中多个 init() 函数,包不相互依赖,多个 init() 函数的执行顺序是根据 main 包中导入顺序确定,从上到下,最后再执行 main 包的 init 函数。
  4、多个包中多个 init() 函数,包存在依赖,多个 init() 函数的执行顺序是根据包导入的依赖关系确定,从里到外,执行顺序为最后被依赖的包(最深层的包:c 包)最先被初始化,如依赖关系 main > a > b > c,则 init 函数执行顺序为 c > b > a > main。
  5、一个包会被多个包同时导入,它只会被导入一次,init() 函数只执行一次。

  在编码时避免依赖 init() 函数的执行顺序。虽然 init() 顺序是明确的,但代码可以更改,init() 函数之间的关系可能会使代码变得脆弱和容易出错,因此在编码时避免依赖 init() 函数的执行顺序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1968620.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL--数据类型

前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除 在MySQL数据库管理系统中,可以通过存储引擎来决定表的类型。同时,MySQL数据库管理系统也提供了数据类型决定表存储数据的类型 …

记录导致计算轮廓面积出错的一个坑点

1.前言 计算轮廓面积是常见的几何算法话题,获取轮廓面积、计算轮廓法线等场景会涉及到。计算轮廓面积的方法有很多,一种常用的是微积分思路的分段求和办法,即组成轮廓的每条线段与X轴或Y轴进行有向投影,轮廓边线与X轴或Y轴的投影之…

【SQL Server】SQL Server基础知识概览

目录 第1章:SQL Server 概览 SQL Server 版本介绍 SQL Server 架构 SQL Server 组件 第1章:SQL Server 概览 SQL Server 版本介绍 SQL Server 是 Microsoft 开发的一款关系型数据库管理系统 (RDBMS),广泛应用于企业级数据存储和处理场景…

Mysql学习-day15

Mysql学习-day15 1. 行列转换 在MySQL中,行列转换可以通过使用CASE语句结合聚合函数来实现。 表t_score数据如图所示 我们想要以学科为列名,展示每个学生的科目成绩,可以先用CASE语句来选出每科的成绩,再进行求和。 选择科目时…

【C++】模拟实现list

🦄个人主页:修修修也 🎏所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 一.了解项目及其功能 📌了解list官方标准 了解模拟实现list 📌了解更底层的list实现 二.list迭代器和vector迭代器的异同 📌迭代…

SSH实现电脑VScode免密登录到虚拟机其原理

在网上想看一下这个原理。发现写的还是比较乱,所以自己总结了一份方便回顾 SSH免密登录的原理主要基于非对称密钥加密技术,比较常用的是RSA算法。 以下是SSH免密登录的详细步骤和原理: 1. 生成密钥对 在客户端上生成一对密钥,…

系统复习Java日志体系

一&#xff0c;我们采用硬编码体验一下几个使用比较多的日志 分别导入几种日志的 jar 包 <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSch…

【已解决】YOLOv8加载模型报错:super().__init__(torch._C.PyTorchFileReader(name_or_buffer))

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

C#——Json数据存储

本文使用的软件为VS2022&#xff0c;不同的软件使用上有些许差异。 C#数据存储 关于数据存储&#xff0c;一般在退出控制台之后&#xff0c;暂存的数据都会释放掉&#xff0c;有没有什么方法能够在下一次进入的时候还能加载上一次的数据呢&#xff1f;答案是有的&#xff0c;关…

利用Arcgis设置分式标注(分子分母标注)

因工作需要&#xff0c;需要设置分式标注&#xff0c;下面详细介绍下如何利用arcgis 设置分式标注&#xff0c;以下操作以供参考&#xff0c;如有疑义可提出。 一、准备工作 软件&#xff1a;arcmap 示例数据&#xff1a;行政区shp矢量图 二、操作步骤 1.添加数据 将行政区sh…

Golang | Leetcode Golang题解之第307题区域和检索-数组可修改

题目&#xff1a; 题解&#xff1a; type NumArray struct {nums, tree []int }func Constructor(nums []int) NumArray {tree : make([]int, len(nums)1)na : NumArray{nums, tree}for i, num : range nums {na.add(i1, num)}return na }func (na *NumArray) add(index, val …

MinIO安装(思路、方法、步骤(以centos8安装为例))

MinIO安装&#xff08;思路、方法、步骤&#xff08;以centos8安装为例&#xff09;&#xff09; 简介 MinIO 是一个高性能、开源的对象存储系统&#xff0c;旨在提供简单、可扩展的存储解决方案&#xff0c;特别适用于大数据、AI 和 ML 应用。 步骤记录 首先我们进入官网看…

前端使用 Konva 实现可视化设计器(19)- 连接线 - 直线、折线

本章响应小伙伴的反馈&#xff0c;除了算法自动画连接线&#xff08;仍需优化完善&#xff09;&#xff0c;实现了可以手动绘制直线、折线连接线功能。 请大家动动小手&#xff0c;给我一个免费的 Star 吧~ 大家如果发现了 Bug&#xff0c;欢迎来提 Issue 哟~ github源码 gitee…

最全个人笔记【Makefile】

1. 基本概念 1.1 make是什么 当一个项目中要编译的文件很多时&#xff0c;手工使用编译器一个个进行编译&#xff0c;很明显不具有可操作性&#xff0c;此时必须借助某些软件&#xff0c;协助我们有序地、正确地自动编译整个工程的所有该编译的文件。这样的软件被称为 工程管…

Simulink|基于粒子群算法的永磁同步电机多参数辨识

目录 主要内容 模型研究 结果一览 下载链接 主要内容 仿真程序参考文献《改进粒子群算法的永磁同步电机多参数辨识》&#xff0c;采用粒子群算法与simulink模型结合的方式&#xff0c;对永磁同步电机进行多参数辨识。程序以定子绕组电阻、d轴电感、q轴电感和永磁…

ai写作免费版工具上哪找?一文详解5大ai写作神器

面对写作难题&#xff0c;你是否曾感到力不从心&#xff1f;从创意枯竭到语法错误&#xff0c;每个写作环节都可能成为挑战。但如今&#xff0c;有了ai写作工具的帮助&#xff0c;这些难题都能迎刃而解。今天&#xff0c;就让我们一起来看看ai写作免费网页版应该怎么选吧&#…

Win11系统文件资源管理器鼠标右键卡顿解决方法

引用链接&#xff1a; Windows 11文件资源管理器崩溃怎么解决&#xff1f;看看这7个解决办法&#xff01;

订单搜索分页查询业务

文章目录 概要整体架构流程技术细节小结 概要 订单搜索分页查询是电商、物流、零售等众多行业中的常见需求&#xff0c;主要用于管理和分析大量订单数据. 需求分析以及接口设计 技术细节 1.Controller层: 根据接口设计来写 ApiOperation("订单搜索")GetMapping(…

使用plink和git进行数据处理

首先使用git进行plink环境配置&#xff0c;显示环境安装成功&#xff0c;在此环境下可以使用plink 在基因型数据处理过程中&#xff0c;看到vcf文件后首要做的就是将vcf文件转成二进制文件&#xff0c;输入命令 plink --vcf genotype.vcf --allow-extra-chr --recode --out tes…

滑动窗口代码实现

public int minSubArrayLen(int target, int[] nums) {int len nums.length;int res len 1;//最大是len&#xff0c;如果最后res结果还是n1说明没有答案返回0int sum0;int left0;for(int right0; right<len; right){sumnums[right];while(sum>target){//不需要判断rig…