返回顶部
首页 > 资讯 > 后端开发 > GO >Go 的入口函数和包初始化的使用
  • 709
分享到

Go 的入口函数和包初始化的使用

2024-04-02 19:04:59 709人浏览 泡泡鱼
摘要

目录包 packagemain.main 函数:Go 应用的入口函数package main注意其他包也可以拥有 main 函数或方法重点引子init 函数:Go 包的初始化函数和

包 package

  • Go 包是 Go 语言的基本组成单元,一个 Go 程序就是一组包的集合,所有 Go 代码都位于包中
  • Go 源码可以导入其他 Go 包,并使用其中的导出语法元素,包括类型、变量、函数、方法等,而且 main 函数是整个 Go 应用的入口函数
  • Go 语言提供了很多内置包,如 fmt、os、io
  • 任何源代码文件必须属于某个包,同时源码文件的第一行有效代码必须是 package 包名语句,通过该语句声明源码文件所在的包

main.main 函数:Go 应用的入口函数

  • Go 语言中有一个特殊的函数:main 包中的 main 函数,也就是 main.main,它是所有 Go 可执行程序的用户层执行逻辑的入口函数
  • Go 程序在用户层面的执行逻辑,会在这个函数内按照它的调用顺序展开

package main

  • 整个 Go 可执行程序中仅允许存在一个名为 main 的包
  • package main想要引用别的包的代码,必须同样以包的方式进行引用
package main

func main() {    
    // 用户层执行逻辑
    ... ...
}

Go 语言要求:可执行程序的 main 包必须定义 main 函数,否则 Go 编译器会报错

注意

main 包是不可以像标准库 fmt 包那样被导入(Import)的

其他包也可以拥有 main 函数或方法

按照 Go 的可见性规则(小写字母卡头的标识符为非导出标识符),非 main包中自定义的 main 函数仅限于包内使用

package pkg1

import "fmt"

func Main () {
    main()
}

func main(){
    fmt.Println("main func for pkg1")
}

重点

  • 一个文件夹下的所有源码文件只能属于同一个包,不要求同名,但还是建议包名和所在目录同名,这样结构更清晰,包名中不能包含特殊符号
  • 给结构定义的方法必须放在同一个包内,可以是不同文件
  • 包名为 main 的包为应用程序的入口包,编译不包含 main 包的源码文件时不会得到可执行文件。
  • 一个文件夹下的所有源码文件只能属于同一个包,属于同一个包的源码文件不能放在多个文件夹下

引子

不过对于 main 包的main 函数来说,还需要明确一点,就是它虽然是用户层逻辑的入口函数,但它却不一定是用户层第一个被执行的函数。这是为什么呢?这跟 Go 语言的另一个函数 init 有关

init 函数:Go 包的初始化函数

和 main.main 函数一样,init 函数也是一个无参数无返回值的函数

func init() {
	// 包初始化逻辑
	... ...
}
  • Go 程序会在这个包初始化的时候,自动调用它的 init 函数,所以 init 函数的执行会发生在 main 函数之前
  • 在 Go 程序中不能手工显式地调用 init,否则会收到编译错误

和 main 函数不一样

  • init 函数在一个包中可以有多个,每个 Go 源文件都可以定义多个 init 函数

init 函数的执行顺序

  • 在初始化 Go 包时,Go 会按照一定的顺序,逐一、顺序地调用这个包的 init 函数
  • 一般来说,先传递给 Go 编译器的源文件中的 init 函数,会先被执行;而同一个源文件中的多个 init 函数,会按声明顺序依次执行

Go 包的初始化次序

  • 从程序逻辑结构角度来看,Go 包是程序逻辑封装的基本单元
  • 每个包都可以理解为是一个“自治”的、封装良好的、对外部暴露有限接口的基本单元
  • 一个 Go 程序就是由一组包组成的,程序的初始化就是这些包的初始化
  • 每个 Go 包还会有自己的依赖包、常量、变量、init 函数(其中 main 包有 main 函数)等

三步走

  • 依赖包按“深度优先”的次序进行初始化
  • 每个包内按以“常量 -> 变量 -> init 函数”的顺序进行初始化
  • 包内的多个 init 函数按出现次序进行自动调用

init 函数的特点

  • 如上图所示,执行顺位排在包内其他语法元素(常量、变量)的后面
  • 每个 init 函数在整个 Go 程序生命周期内仅会被执行一次
  • init 函数是顺序执行的,只有当一个 init 函数执行完毕后,才会去执行下一个 init 函数

init 函数的用途

重置包级变量值

init 函数就好比 Go 包真正投入使用之前唯一的“质检员”,负责对包内部以及暴露到外部的包级数据(主要是包级变量)的初始状态进行检查

实现对包级变量的复杂初始化

有些包级变量需要一个比较复杂的初始化过程,有些时候,使用它的类型零值或通过简单初始化表达式不能满足业务逻辑要求,而 init 函数则非常适合完成此项工作,标准库 Http 包中就有这样一个典型示例

package main

import (
	"os"
	"strings"
)

var (
	http2VerboseLogs    bool // 初始化默认值 false
	http2logFrameWrites bool
	http2logFrameReads  bool
	http2inTests        bool
)

func init() {
	e := os.Getenv("GODEBUG")
	if strings.Contains(e, "http2debug=1") {
		http2VerboseLogs = true // 在 init 中对 http2VerboseLogs 的值进行重置
	}
	if strings.Contains(e, "http2debug=2") {
		http2logFrameWrites = true
		http2logFrameReads = true
		http2inTests = true
	}
}

http 包在init 函数中,就根据环境变量 GODEBUG 的值,对这些包级开关变量进行了复杂的初始化,从而保证了这些开关变量在 http 包完成初始化后,可以处于合理状态

在 init 函数中实现“注册模式”

来看一段使用 lib/pq 包访问 postgresql 数据库的代码 ?

package main

import (
	"database/sql"
	"log"

	_ "GitHub.com/lib/pq"
)

func main() {
	db, err := sql.Open("postgres", "user=pqgotest dbname=pqgotest sslmode=ver)
	if err != nil {
		log.Fatal(err)
	}
	age := 21
	rows, err := db.Query("SELECT name FROM users WHERE age = $1", age)
}
复制代码

这里是以空导入_的方式导入 lib/pq 包的,main 函数中没有使用 pq 包的任何变量、函数或方法,这样就实现了对 PostgreSQL数据库的访问

实际原因

在 pq 包的 conn.go 源码文件中的 init 函数

func init() {
	sql.ReGISter("postgres", &Driver{})
}
  • 利用了空导入的特性,将 lib/pq 包作为 main 包的依赖包,在包初始化时,会先执行 lib/pq 包里面的 init 函数
  • pq 包的 init 函数将自己实现的 sql 驱动注册到了 sql 包中
  • 这样在实际应用代码中,Open 数据库时,传入驱动名字(这里是 postgres),就能得到数据库实例,然后对数据库进行操作,实际上是因为调用了 pq 包中相应的驱动实现的

好处:这种通过在 init 函数中注册自己的实现的模式,就有效降低了 Go 包对外的直接暴露,尤其是包级变量的暴露,从而避免了外部通过包级变量对包状态的改动

工厂设计模式

从标准库 database/sql 包的角度来看,这种“注册模式”实质是一种工厂设计模式的实现,sql.Open 函数就是这个模式中的工厂方法,它根据外部传入的驱动名称“生产”出不同类别的数据库实例句柄

通过注册模式实现获取各种格式图片的宽、高

Go 源码

package main

import (
	"fmt"
	"image"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"os"
)

func main() {
	// 支持 png、jpeg、gif
	width, height, err := imageSize(os.Args[1])
	if err != nil {
		fmt.Println("get image size error:", err)
		return
	}
	fmt.Printf("image size: [%d,%d]\n", width, height)
}

func imageSize(imageFile string) (int, int, error) {
    // 打开图片文件
	f, _ := os.Open(imageFile)
	defer f.Close()
	
    // 对文件进行解码,得到图片实例
	img, _, err := image.Decode(f)
	if err != nil {
		return 0, 0, err
	}
	
    // 返回图片区域
	b := img.Bounds()
	return b.Max.X, b.Max.Y, nil
}
  • 上面的源码支持 png、jpeg、gif 三种格式的图片
  • 但并不需要手动支持图片格式
  • 是因为 image/png、image/jpeg 和 image/gif 包都在各自的 init 函数中,将自己“注册”到 image 的支持格式列表中了
// $GOROOT/src/image/png/reader.go
func init() {
	image.RegisterFORMat("png", pngHeader, Decode, DecodeConfig)
}

// $GOROOT/src/image/jpeg/reader.go
func init() {
	image.RegisterFormat("jpeg", "\xff\xd8", Decode, DecodeConfig)
}

// $GOROOT/src/image/gif/reader.go
func init() {
	image.RegisterFormat("gif", "GIF8?a", Decode, DecodeConfig)
}

到此这篇关于Go 的入口函数和包初始化的使用的文章就介绍到这了,更多相关Go 入口函数和包初始化内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

您可能感兴趣的文档:

--结束END--

本文标题: Go 的入口函数和包初始化的使用

本文链接: https://lsjlt.com/news/148355.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Go 的入口函数和包初始化的使用
    目录包 packagemain.main 函数:Go 应用的入口函数package main注意其他包也可以拥有 main 函数或方法重点引子init 函数:Go 包的初始化函数和 ...
    99+
    2024-04-02
  • Go中import导入包和变量初始化的方法
    这篇“Go中import导入包和变量初始化的方法”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Go中import导入包和变量...
    99+
    2023-06-30
  • Go基础教程系列之import导入包(远程包)和变量初始化详解
    import导入包 搜索路径 import用于导入包: import ( "fmt" "net/http" "mypkg" ) 编译器会根据上面指定的相对路...
    99+
    2024-04-02
  • 深入理解Go语言init方法:探讨初始化函数的执行顺序
    Go语言中的init()函数是一种特殊的函数,它会在程序执行时被自动调用,用于执行一些初始化操作。在本文中,我们将深入理解Go语言中init()方法的执行顺序,并通过具体的代码示例来展...
    99+
    2024-04-02
  • 怎么使用c#构造函数初始化列表
    在C#中,可以使用构造函数初始化列表来初始化类的成员变量。构造函数初始化列表是在构造函数的参数列表后面使用冒号来定义的。下面是一个示...
    99+
    2023-09-28
    c#
  • jquery初始化函数的方法有哪些
    在 jQuery 中,有以下几种方法可以用来初始化函数:1. $(document).ready(function(){...}):...
    99+
    2023-08-09
    jquery
  • python中包初始化的作用是什么
    python中包初始化的作用是什么?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。python是什么意思Python是一种跨平台的、具有解释性、编译性、互动性和面...
    99+
    2023-06-14
  • 应用启动数据初始化接口CommandLineRunner和Application详解
    目录应用启动数据初始化接口CommandLineRunner和Application详解1 运行时机2 实现接口3 执行顺序4 设置启动参数5 运行效果ApplicationRunn...
    99+
    2024-04-02
  • Go语言main函数详解:入口函数的作用和意义
    Go 语言主要被用于构建高效的、可靠的分布式系统。在 Go 语言中,每个可执行程序都包含一个名为 main 的包和一个名为 main 的函数。main 函数是整个程序的入口函数,它的作...
    99+
    2024-04-02
  • 怎么使用初始化种子和unfold迭代函数生成列表
    这篇文章主要介绍“怎么使用初始化种子和unfold迭代函数生成列表”,在日常操作中,相信很多人在怎么使用初始化种子和unfold迭代函数生成列表问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么使用初始化种子...
    99+
    2023-06-25
  • C++构造函数的初始化列表详解
    目录1.问题2.解决方法(初始化列表)3.顺序问题总结 1.问题 class A { private: int m_a; public: A(int a) { cout ...
    99+
    2024-04-02
  • k8s在go语言中的使用及client 初始化简介
    作为k8s官方维护的客户端,k8s go-client对于go语言中使用k8s可以说是唯一选项。但是官方的使用示例我个人觉得并不是很清晰,尤其是对于对于k8s并不熟悉的用户。这里我总...
    99+
    2024-04-02
  • 如何使用Go语言打包接口函数?
    Go语言是一门快速发展的编程语言,它已成为许多开发者的首选语言。在Go语言中,打包接口函数是一项非常重要的任务。本文将介绍如何使用Go语言打包接口函数。 一、什么是打包接口函数? 打包接口函数是将多个接口函数打包成一个大的接口函数。打包后的...
    99+
    2023-09-20
    打包 接口 函数
  • Swift初始化方法的使用介绍
    目录初始化初始化器初始化器的相互调用两段式初始化安全检查重写自动继承初始化 初始化器 1、类、结构体、枚举都可以定义初始化器 class Size { init(age: I...
    99+
    2024-04-02
  • C++是怎么构造函数的初始化列表
    C++是怎么构造函数的初始化列表,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1.问题class A {private:int m_a;publi...
    99+
    2023-06-22
  • C++之谈谈构造函数的初始化列表
    目录一、引入二、初始化的概念区分三、语法格式及使用四、注意事项【⭐】五、总结与提炼一、引入 我们知道,对于下面这个类A的成员变量_a1和_a2属于【声明】,还没有在内存中为其开辟出一...
    99+
    2023-05-15
    C++构造函数 C++构造函数初始化列表 C++初始化列表
  • C++深入讲解初始化列表的用法
    目录一、小问题二、类成员的初始化三、类中的 const 成员四、初始化与赋值的不同五、小结一、小问题 下面的类定义是否合法 如果合法,ci 的值是什么,存储在哪里 下面编写代码一探...
    99+
    2024-04-02
  • C#数组初始化的应用方法
    本篇内容介绍了“C#数组初始化的应用方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!C#数组初始化的应用在数据压缩方面的应用是什么呢?我们...
    99+
    2023-06-17
  • Perl中二维数组的引用和初始化方法
    这篇文章主要讲解了“Perl中二维数组的引用和初始化方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Perl中二维数组的引用和初始化方法”吧!Perl二维数组的引用和初始化Perl二维数组...
    99+
    2023-06-17
  • ABAP和Go语言的初始化操作方法是什么
    本篇内容主要讲解“ABAP和Go语言的初始化操作方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“ABAP和Go语言的初始化操作方法是什么”吧!# ABAPGo作用同上:Kubernete...
    99+
    2023-06-03
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作