GO 语言的函数??

简介: 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 的目录结构如下

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

具体的 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 的分享细节

欢迎点赞,关注,收藏

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

好了,本次就到这里

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

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

可以进入地址进行体验和学习:https://xxetb.xet.tech/s/3lucCI

相关文章
|
1天前
|
Java 编译器 Go
探索Go语言的性能优化技巧
在本文中,我们将深入探讨Go语言的底层机制,以及如何通过代码层面的优化来提升程序性能。我们将讨论内存管理、并发控制以及编译器优化等关键领域,为你提供一系列实用的技巧和最佳实践。
|
1天前
|
Cloud Native Go API
Go语言在微服务架构中的创新应用与实践
本文深入探讨了Go语言在构建高效、可扩展的微服务架构中的应用。Go语言以其轻量级协程(goroutine)和强大的并发处理能力,成为微服务开发的首选语言之一。通过实际案例分析,本文展示了如何利用Go语言的特性优化微服务的设计与实现,提高系统的响应速度和稳定性。文章还讨论了Go语言在微服务生态中的角色,以及面临的挑战和未来发展趋势。
|
1天前
|
安全 Go 调度
探索Go语言的并发模式:协程与通道的协同作用
Go语言以其并发能力闻名于世,而协程(goroutine)和通道(channel)是实现并发的两大利器。本文将深入了解Go语言中协程的轻量级特性,探讨如何利用通道进行协程间的安全通信,并通过实际案例演示如何将这两者结合起来,构建高效且可靠的并发系统。
|
1天前
|
安全 Go 开发者
破译Go语言中的并发模式:从入门到精通
在这篇技术性文章中,我们将跳过常规的摘要模式,直接带你进入Go语言的并发世界。你将不会看到枯燥的介绍,而是一段代码的旅程,从Go的并发基础构建块(goroutine和channel)开始,到高级模式的实践应用,我们共同探索如何高效地使用Go来处理并发任务。准备好,让Go带你飞。
|
2天前
|
运维 Go 开发者
Go语言在微服务架构中的应用与优势
本文深入探讨了Go语言在构建微服务架构中的独特优势和实际应用。通过分析Go语言的核心特性,如简洁的语法、高效的并发处理能力以及强大的标准库支持,我们揭示了为何Go成为开发高性能微服务的首选语言。文章还详细介绍了Go语言在微服务架构中的几个关键应用场景,包括服务间通信、容器化部署和自动化运维等,旨在为读者提供实用的技术指导和启发。
|
2天前
|
安全 Go 调度
探索Go语言的并发之美:goroutine与channel
在这个快节奏的技术时代,Go语言以其简洁的语法和强大的并发能力脱颖而出。本文将带你深入Go语言的并发机制,探索goroutine的轻量级特性和channel的同步通信能力,让你在高并发场景下也能游刃有余。
|
3天前
|
Go 开发者
Go语言中的并发编程:从基础到实践
在当今的软件开发中,并发编程已经成为了一项不可或缺的技能。Go语言以其简洁的语法和强大的并发支持,成为了开发者们的首选。本文将带你深入了解Go语言中的并发编程,从基础概念到实际应用,帮助你掌握这一重要的编程技能。
|
4天前
|
Go
使用go语言将A助手加入项目中
使用go语言将A助手加入项目中
14 2
|
6天前
|
负载均衡 Go API
探索Go语言在微服务架构中的应用与优势
在这篇技术性文章中,我们将深入探讨Go语言(又称为Golang)在构建微服务架构时的独特优势。文章将通过对比分析Go语言与其他主流编程语言,展示Go在并发处理、性能优化、以及开发效率上的优势。同时,我们将通过一个实际的微服务案例,详细说明如何利用Go语言构建高效、可扩展的微服务系统。
|
4天前
|
Go 数据处理 调度
Go语言中的并发模型:解锁高效并行编程的秘诀
本文将探讨Go语言中独特的并发模型及其在现代软件开发中的应用。通过深入分析 Goroutines 和 Channels,我们将揭示这一模型如何简化并行编程,提升应用性能,并改变开发者处理并发任务的方式。不同于传统多线程编程,Go的并发方法以其简洁性和高效性脱颖而出,为开发者提供了一种全新的编程范式。