一、init函数
(一)简介
每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用,也就是说init会在main函数前调用。
package main import "fmt" // init函数,通常可以在init函数中完成初始化工作 func init() { fmt.Println("init...") } func main() { fmt.Println("main...") } /*
输出:
init... main... */
(二)单包中变量、init、main中的调用
如果一个文件同时包含全局变量定义、init函数和main函数,则执行的流程是:全局变量定义--》init函数--》main函数
package main import "fmt" var age = test() // 通过此函数可以看到是否调用变量 func test() int { fmt.Println("test...") return 12 } func init() { fmt.Println("init...") } func main() { fmt.Println("main...") } /* 输出: test... init... main... */
(三)多包中变量、init、main中的调用
多包组织:
- utils.go
package utils import "fmt" var Age int // Age 全局变量,在init函数中进行初始化 func init() { fmt.Println("utils包init...") Age = 20 }
- main.go
package main import ( "fmt" "go_tutorial/day06/initFunc/03/utils" //引入utils包 ) var age = test() func test() int { fmt.Println("test...") return 12 } func init() { fmt.Println("init...") } func main() { fmt.Println("main...") fmt.Println("init...",utils.Age) } /* 输出 utils包init... test... init... main... init... 20 */
如果main.go和utils.go中如果由变量定义、init函数,那么执行顺序是怎么样的呢?
main.go中先引入utils包,所以会执行utils.go中的变量定义、init函数。
二、匿名函数
(一)局部匿名函数
Go支持匿名函数,匿名函数就是没有名字的函数。
- 使用方式一
在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次。
package main import "fmt" func main() { res := func(n1 int, n2 int) int { return n1 + n2 }(5, 10) fmt.Println(res) }
- 使用方式二
将匿名函数赋值给一个变量,然后通过变量的方式进行调用。
package main import "fmt" func main() { a := func(n1 int, n2 int) int { return n1 + n2 } res := a(10, 5) fmt.Println(res) }
(二)全局匿名函数
如果将匿名函数赋值给一个全局变量,那么这个匿名函数就成为一个全局匿名函数。
package main import "fmt" var ( Func = func(n1 int, n2 int) int { return n1 + n2 } ) func main() { // 全局匿名函数调用 res := Func(10, 5) fmt.Println(res) }
三、闭包
闭包就是一个函数与其相关的引用环境组成的一个整体。
package main import "fmt" // 累加器 func AddUpper() func(int) int { var n int = 5 return func(x int) int { n = n + x return n } } func main() { f := AddUpper() fmt.Println(f(1)) fmt.Println(f(2)) } /* 输出: 6 8 */
AddUper是一个函数,它的返回值是一个匿名函数,这里匿名函数与变量n组成的就是一个闭包。闭包可以保存上次引用的n值,而不用反复传入。
四、defer
在函数中,程序员经常需要创建资源(如:数据库连接、文件句柄等),为了在函数执行完毕后,及时释放资源,Go中提供defer延时机制。
package main import "fmt" func sum(n1 int, n2 int) int { // 当执行当defer会被延迟执行,先执行defer后面的语句 // defer的语句会被压入到栈中,按照先如入后出的方式出栈 // 当sum函数执行完毕后会执行defer defer fmt.Println("sum n1=", n1) // 第三步 defer fmt.Println("sum n2=", n2) // 第二步 res := n1 + n2 fmt.Println("sum res=", res) // 第一步 return res } func main() { res := sum(5, 10) fmt.Println("main res=", res) // 第四步 } /* 输出: sum res= 15 sum n2= 10 sum n1= 5 main res= 15 */
- 当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句到一个栈中,然后继续执行函数下一个语句
- 当函数执行完毕后,再从defer栈中,一次从栈顶取出语句执行
- 在defer将语句放入到栈中时,也会将相关的值拷贝同时入栈
defer主要的价值就是当函数执行完毕后可以及时释放函数创建的资源,如:
package main import "fmt" func test1() { // 关闭文件资源 f := openfile("filePath") defer f.close() // 操作文件代码 // ... } func test2() { // 关闭数据库资源 connect := openDatabase("connect path") defer connect.close() // 操作数据库代码 // ... } func main() { }