Go核心开发学习笔记(十四) —— init函数,匿名函数,闭包,defer

init函数

  1. 每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用。

  2. 一般用来做main函数运行前的初始化工作。

  3. 一个文件同时包含全局变量定义,init(),main(),那么执行顺序为: 全局变量定义->init()->main()。

  4. 第3条加强版,在src非main文件夹中,写一个函数,引入两个变量,并且写入init()函数,所以此时的顺序变成:
    非main的import包中的init() -> main中的全局变量定义 -> init() -> main() 。

  5. 第4条加强版,如果src非main被调用函数中存在 全局变量定义和init(),src main文件中包含全局变量定义、init(),main(),这五项应该是什么执行顺序:
    非main的import包中的全局变量定义 -> 非main的import包中的init() -> main中的全局变量定义 -> init() -> main() 。

    请参照下图示意

    package main 
    import "<被调用函数>"    //①全局变量定义 ②init()                      
    ③ <全局变量定义>
    ④ func init {
          \<函数体>
    }
    ⑤func main {
    		 \<函数体>
    }
    

匿名函数:

  1. 使用场景:一般只使用一次就OK,调用一次就好了
  2. 定义匿名函数时就直接调用,这种方式只能调用一次,案例演示:
    package main
    
    import "fmt"
    
    func main() {
    	//直接在创建匿名函数时直接调用,注意下匿名函数格式,传参在函数定义后直接传
    	res := func (n1,n2 int) int {
    		return n1+n2
    	} (10,20)
    	fmt.Println(res)
    }
    
  3. 可以将匿名函数赋予一个变量,定义好形参之后由 <变量>(para1,para2) 方式使用。
  4. 在全款变量中定义一个变量,数据类型为匿名函数,同上可以,main()中任意一个地方都可以使用该匿名函数。
  5. 匿名函数的优点是在main()函数中如果定义其他有名称的函数是不允许的,所以匿名函数灵活性很高。

闭包

  1. 闭包就是一个函数与其相关的引用环境组成的一个整体。
  2. 闭包返回值是一个函数,这个函数会用到函数外的一些变量,它们共同组成一个整体,这个整体叫闭包。
  3. 可以这样理解,闭包是一个类,外部函数中的变量看做定义属性,返回函数值看成定义方法,属性和方法共同构成类,即闭包。
  4. 搞清楚闭包的关键,就是分析出返回函数和它引用到的变量,共同构成闭包,每次调用时函数外环境变量不是重新初始化,而是累加。
    案例:
    package main
    
    import "fmt"
    
    func Addpp() func(int) int {       //定义一个函数 传入的参数是一个func, 返回值为int
    	var n int = 20
    	var str string = "shit!"
    	return func (x int) int {      //Addpp返回值是一个匿名函数,相当于一个累加器了
    		n = n + x
    		str += " damn!"
    		fmt.Println("str= ",str)
    		return n
    	}
    }
    func main() {
    	/*
    		理解闭包的概念,闭包本身是一个函数,闭包的返回值也是一个函数,return func;
    		return 的是一个匿名函数,匿名函数中会调用闭包函数中的变量,从而做一些事情,产生返回值;
    		被匿名函数返回的返回值是匿名函数外的变量,则变量会改变初始化的值
    	*/
    	f := Addpp()
    	fmt.Println(f(1))
    	fmt.Println(f(2))
    	fmt.Println(f(3))
    }
    
  5. 案例:使用闭包完成传入一个文件名,如果文件名有后缀.jpg就不用修改,直接输出;如果没有后缀就将文件名加上.jpg输出:
    判断文件名是否有后缀,使用strings.HasSuffix()
    代码如下:
    package main
    import (
    	"fmt"
    	"strings"           //使用strings包的HasSuffix方法判断文件是否有后缀
    )
    
    func makeSuffix (suffix string) func (string) string {            //makeSuffix()实现功能是添加后缀名,返回值为一个函数
    	return func (fileName string) string {                        //
    		if !strings.HasSuffix(fileName,suffix) {
    			fileName += suffix
    			return fileName
    		}
    		return fileName
    	}
    }
    
    func main() {
    	/*
    	案例:使用闭包完成传入一个文件名,如果文件名有后缀.jpg就不用修改,直接输出;如果没有后缀就将文件名加上.jpg输出:
    	         判断文件名是否有后缀,使用strings.HasSuffix()
    	 */
    	var fileName string
    	fmt.Println("请输入传入值的文件名称: ")
    	fmt.Scanf("%s",&fileName)
    	f := makeSuffix(".avi")
    	fmt.Println(f(fileName))
    }
    

defer
在函数中,程序员经常需要创建资源(数据库连接,锁,句柄等),为了在函数执行完成后,及时释放资源,打开,建立连接后立刻defer xxclose

  1. 当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈(defer栈)。
  2. 当函数执行完毕后,再从defer栈,按照先进后出的方式出栈,执行。
  3. 将语句压栈时,也会把相关的值拷贝到栈中,例如defer后的语句是n1++等改变数值的,根据压栈时间,n1后续不会++,与值传递函数一样。
    示例:
    package main
    import "fmt"
    func sum (n1,n2 int) int {                
    	defer fmt.Println("n2 is OK",n2)         //压栈,辅函数中输入defer,函数结束后本条第二个执行
    	defer fmt.Println("n1 is OK",n1)         //压栈,根据压栈方式可以判定,函数结束后本条第一个执行
    
    	res := n1 + n2
    	fmt.Println("sum is finished")        //终端最先输出的是这句话
    	return res
    }
    func main() {
    	f := sum(10,20)
    	fmt.Println("res的值为",f)             //最后输出的是这句话
    }
    
  4. 通过上面的示例可以看出,如果学过k8S就能理解pod在启动前有个pre-start,后续有个post-stop, 一个是pods启动前,需要配置的东西,另外一个是pods终止后,需要做什么。
  5. defer最主要的价值是函数执行完毕后,及时释放函数创建的资源,例如连接数据库,或者打开文件,立马defer输入资源关闭,这样安心处理事情,函数处理完了自然资源就关闭了,设计理念很好。
    模拟:
    func test {
    file = openfile(<文件名>)
    defer file.close() //立刻写,不会影响函数执行时或持久连接而close
    }

Tips:

  1. func sum (n1,n2 float32) {…} //注意这种方式使用没有问题,n1也是float32格式,不会出现编译错误。

  2. 被调用函数尽量根据需求是否改变形参为值传递还是引用传递,如果是值传递直接用基本变量类型,如果是引用传递则使用指针。

    案例:写一个函数比较三个数大小,按照从小到大输出

    被调用函数:

    package compare
    func Compare(a *int,b *int,c *int)  {
        var temp int
    	if *a > *b {
    		temp = *a
    		*a = *b
    		*b = temp
    	}
    	if *a > *c {
    		temp = *a
    		*a = *c
    		*c = temp
    	}
    	if *b > *c {
    		temp = *b
    		*b = *c
    		*c = temp
    	}
    }
    

    调用函数:

    package main
    
    import (
    	"compare"
    	"fmt"
    )
    
    func main() {
    	var (
    		a int
    		b int
    		c int
    	)
    	fmt.Println("请输入三个数,用空格间隔开:\n")
    	fmt.Scanf("%d %d %d",&a,&b,&c)
    	compare.Compare(&a,&b,&c)
    	fmt.Printf("三个数从小到大排列为:%v %v %v",a,b,c)
    }
    
发布了50 篇原创文章 · 获赞 18 · 访问量 4015

猜你喜欢

转载自blog.csdn.net/weixin_41047549/article/details/89714567