zoukankan      html  css  js  c++  java
  • Go语言之函数进阶

    一、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() {
        
    }
    作者:iveBoy
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    后缀运算符与前缀运算符的区别
    冒泡排序
    pc端遇到的知识点
    Windows 上传文件到Android 模拟器报错 Failed to push selection: Invalid argument
    Android错误 transfer error: Readonly file system
    Android双击返回键退出Activity
    Android应用添加(创建)和删除及判断是否存在桌面快捷方式
    Aandroid 总结4种线程中操作UI界面的方法
    Could not load file or assembly 'System.Data.SQLite' or one of its dependencies. An attempt was made to load a program
    学习
  • 原文地址:https://www.cnblogs.com/shenjianping/p/15581138.html
Copyright © 2011-2022 走看看