GO 语言的函数??

函数是什么?

学过编程的 xdm 对于函数自然不会陌生,那么函数是什么呢?

函数是一段可以重用的代码块,可以被多次调用,我们可以通过使用函数,提高咱们代码代码的模块化,提高程序的可读性和可维护性

对 GO 语言中的函数,入参可以有多个,返回值也可以有多个,此处建议,入参不大于 5 个,如果超过 5 个最好使用传入结构的方式来进行处理

程序的初始化顺序?

要了解 GO 语言中程序的初始化顺序,就要先明白整个程序初始化流程中,都会涉及到哪一些内容

GO 语言中以包 package 作为 程序逻辑封装的基本单元,每一个包我们可以理解为他是独立的,封装良好的,并且对外暴露了接口的基本单元

然而,咱们的 GO 程序就是由这些包组成的,那么这个包里面一般又包含着 基本的常量,变量,函数,类型,方法,和接口等等

那么对于上述这些元素,我们就要弄明白在包里面他们是如何有序的进行初始化的

本章主要分享函数,GO 语言中除了 main 函数,还有一个特殊的函数就是 init 函数

fun init() {
    // 具体的实现
    // 具体的初始化
}

这里可以看到init 函数,是没有入参,也没有返回值的函数

init 函数用于在本包中进行初始化和做一些程序初始状态的检查工作,例如我们会把一些单例,数据库句柄,各种连接句柄放到 init 函数中进行初始化,init 函数在整个程序生命周期,只会被调用一次

且这里我们需要注意,一个包里面会有多个 GO 语言源文件,这些源文件中都可以定义 init 函数,但是在程序执行 init 函数时,是一个一个的去执行的,而不是并发的去执行的,那么此处的执行顺序我们就不要去过度依赖,按照我们使用惯例来看,过度依赖 init 函数的执行顺序可能会出现意想不到的问题

所以此处我们就需要注意,如果不同的 init 函数实现中,相互有依赖,那么可能就会导致程序出现我们不期望的结果

那么整个程序的初始化流程和顺序我们要知道是这样子的:

对于一个包而言,初始化顺序如下

  1. 包内的包级常量
  2. 包内的包级变量
  3. 包内的 init 函数

对于一个程序里面,从 main 函数开始,必然是包含了其他的子包,那么初始化的时候是怎么样的呢?

  1. 先是初始化子包,若子包里面还有子包,那么就继续从最深一层的子包开始按照包里面的初始化顺序进行
  2. 最深一层的子包按照顺序初始化完毕之后,就到了他的父级包开始按照顺序初始化
  3. 走到 main 包中的 包级常量,包级变量, init 函数进行初始化
  4. 最终,执行 main 函数

通过上图是不是对于 GO 程序初始化顺序更加清晰了呢,如果有表述不当的地方,还请多多评论留言,多多指教

我们可以写一个 demo 来看看效果:

demo 的目录结构如下

[图片上传失败...(image-ecb6b0-1697642096055)]

main.go 文件的内容如下:

package main

import "fmt"
import _ "ttt/p1"
import _ "ttt/p2"

var (
   _ = checkConst()
   c = varInit(4)
   d = varInit(5)
)

const (
   a = 2
   b = 3
)

func init() {

   fmt.Println("main : init")

}

func checkConst() int {
   if a == 2 {
      fmt.Println("main : const a")
   }
   if b == 3 {
      fmt.Println("main : const b")
   }
   return 0
}

func varInit(x int) int {
   fmt.Println("main : var ", x)
   return x
}

func main() {
   fmt.Println("main : main ")
}
  1. main 包中,我们导入了 p1 和 p2 包,main 包中定义了 const 常量和变量,以及 对应的 init 函数
  2. p1 和 p2 包的内容和 main 的大体一直

运行程序之后,我们可以看到打印的结果如下, 通过这个 demo 我们就可以看到程序的初始化顺序正如上所述

p1 : const a
p1 : const b
p1 : var  4
p1 : var  5
p1 : init
p2 : const a
p2 : const b
p2 : var  4
p2 : var  5
p2 : init
main : const a
main : const b
main : var  4
main : var  5
main : init
main : main

[图片上传失败...(image-2f0460-1697642096055)]

具体的 demo 仓库可以查看如下地址:

GO 语言程序初始化顺序 demo

函数在 GO 语言中的地位?

在 GO 中,我们可以看到没有那些高级语言面向对象的语法,例如 Class 类,继承,对象等等内容, GO 语言中可以通过函数或者方法的方式进行各种组合完成我们想做的一切事项

此处就体现了函数在 GO 语言中是极其的重要,函数在 GO 语言中是 一等公民 的存在

如何体现 一等公民呢?函数在 GO 中可以像普通类型的值一样被创建和被使用,使用起来非常灵活和自由

例如,创建的函数可以存储在变量中,也可以做为其他函数的返回值(在函数内部创建了函数变量之后,通过返回值返回),还可以作为其他函数的参数进行传递

那么我们就来写一些 demo 查看这个 一等公民 是有多么的自由吧:

  1. 正常写一个 函数,简单实现
func helloworld() string{
    name := "阿兵云原生"
    return name
}
  1. 函数作为入参
func add(a, b int) int {
   return a + b
}

func cal(a int, f func(int, int) int) int {
   return a + f(a, a)
}
  1. 函数作为返回值返回出去
func add(a, b int) int {
   return a + b
}

func getFunc() func(int, int) int{
   return add
}
  1. 将函数存储在变量中使用
type TestFunc struct {
   f    func(int, int) int
   name string
}

func add(a, b int) int {
   return a + b
}

func main() {
  
   tt := &TestFunc{f: add, name: "阿兵云原生"}
   fmt.Println(tt.f(1,2))
   
}
  1. 函数作为类型来使用
type PFunc func(int) int

综上所述,对于使用 GO 语言中的函数还是相当方便的,用起来是相当的顺手

那么对于普通类型的数据,我们可以进行显示的类型转换,那么对于函数是不是也可以??

自然是可以的,我们可以来看一个 一般类型的 demo:

var x int = 10
var y int32 = 20
fmt.Println(x + y)

上述代码很明显 x 的类型和 y 的类型是不同的, GO 语言是不会编译通过的,我们需要将其中一个变量的类型显示的转换才可,例如 fmt.Println(int32(x) + t)

再来看看显示转换函数的 demo:

  1. 写一个 Processer 接口,有一个 Do(int) int 接口
  2. 定义一个 PFunc 类型对应 func(int) int
  3. 写一个普通函数和 func playfootball(x int) int
  4. 在 main 函数中,我们直接将 playfootball 赋值给 Processer 看看效果
type Processer interface {
   Do(int) int
}

type PFunc func(int) int

func (f PFunc) Do(x int) int {
   return f(x)
}

func playfootball(x int) int {
   return x
}

func main() {

   var i Processer = playfootball

   fmt.Println(i.Do(20))
}

运行上述代码,很明显是编译不过的,因为 playfootball 的类型和并没有实现 Do 接口,虽然入参和返回值的类型和个数都一样

我们只需要将上述代码,将 playfootball 显示转换,写成 PFunc(playfootball) 即可顺利通过编译,正常看到打印

因为上述 PFunc 类型实现了 Processer 接口,因此对于 i 需要接收的是 PFunc 的实例,这个时候对 playfootball 进行显示转换后,实际上最终调用的函数 playfootball 函数

GO 语言中的 defer

使用过 GO 语言的 xdm 对于 defer 不会陌生,对于那些我们需要在函数退出前释放或者需要关闭的资源,我们就可以使用到 defer 这里用起来就相当的省心,哪怕函数中出现了 panic,defer 也能给你守护的明明白白的

func test() {
   defer func() {
      if e := recover(); e != nil {
         fmt.Println(e)
         fmt.Println("recover ... ")
      }
   }()

   panic("panic ...")
}

func main() {
   test()
}

例如上述 demo ,会正常输出,不会 panic ,因为已经被捕获和处理了

使用 defer 能大大的减少我们的开发人员的心智负担,例如我们以前在使用锁的时候,加锁之后,我们可能会忘记写解锁的语句,可使用了 defer ,你完全可以是在加锁的时候,使用 defer 让函数关闭的时候解锁即可,当然具体逻辑还是要看具体的实现

关于 defer 的原理,以及使用 defer 的注意事项就不在过多赘述,可以查看如下文章获得答案

  1. GO 中的 defer 有哪些注意事项?下
  2. GO 中的 defer 有哪些注意事项?上
  3. GO 中 defer的实现原理

总结:

本次分享了函数相关的基本知识,以及 GO 程序的初始化顺序,对于 defer 的使用有想法的欢迎点击上述连接查看具体 defer 的分享细节

欢迎点赞,关注,收藏

朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

[图片上传失败...(image-42881a-1697642096055)]

好了,本次就到这里

技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

我是阿兵云原生,欢迎点赞关注收藏,下次见~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,313评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,369评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,916评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,333评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,425评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,481评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,491评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,268评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,719评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,004评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,179评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,832评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,510评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,153评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,402评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,045评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,071评论 2 352

推荐阅读更多精彩内容

  • 函数 Go语言函数格式func 函数名称(形参列表)(返回值列表){函数语句} Go语言函数的类型没有返回值没有形...
    喝酸奶要舔盖__阅读 393评论 0 0
  • 函数 Go语言和C语言一样也有函数的概念, Go语言中函数除了定义格式和不用声明以外,其它方面几乎和C语言一模一样...
    极客江南阅读 820评论 3 6
  • 只描述和C语言中函数的区别 Go语言函数不需要声明 Go语言的函数是一等公民,和变量一样,可以赋值给某一变量(匿名...
    AuglyXu阅读 475评论 0 0
  • GO语言函数 GO语言是由函数组成的 没有返回值没有形参的函数 有返回值没有形参的函数 没有返回值有形参的函数 有...
    超级皮波9阅读 261评论 0 0
  • 函数包含函数名、行参列表、函数体和返回值列表,使用func进行声明,函数无参数或返回值时则形参列表和返回值列表省略...
    imsilence阅读 193评论 0 0