• defer、异常处理、import


    目录:

    1:defer延迟执行

    2:异常处理

    3:import规则

    一、defer延迟执行

    Go语言的 defer 语句会将其后面跟随的语句进行延迟处理

    defer特性:

    1. 关键字 defer 用于注册延迟调用。

    2. 这些调用直到 return 前才被执。因此,可以用来做资源清理。

    3. 多个defer语句,按先进后出的方式执行。

    4. defer语句中的变量,在defer声明时就决定了。

    defer的用途:

    1. 关闭文件句柄

    2. 锁资源释放

    3. 数据库连接释放

    go 语言的defer功能强大,对于资源管理非常方便,但是如果没用好,也会有陷阱。

    案例1:统一函数执行耗时时间是多少

    • Go 语言中所有的函数调用都是传值的

    • 调用 defer 关键字会立刻拷贝函数中引用的外部参数 ,包括start 和time.Since中的Now

    • defer的函数在压栈的时候也会保存参数的值,并非在执行时取值

    解决问题:

    package main
    
    import (
    	"log"
    	"time"
    )
    
    func main() {
    	start := time.Now()
    	log.Printf("开始时间为:%v", start)
    	defer func() {
    		log.Printf("开始调用defer")
    		log.Printf("时间差:%v", time.Since(start))
    		log.Printf("结束调用defer")
    	}()
    	time.Sleep(3 * time.Second)
    
    	log.Printf("函数结束")
    }
    

    因为拷贝的是函数指针,函数属于引用传递

    案例2:按顺序打印数值

    package main
    
    import "fmt"
    
    func main() {
    	var whatever = [5]int{1,2,3,4,5}
    	for i,_ := range whatever {
            //函数正常执行,由于闭包用到的变量 i 在执行的时候已经变成4,所以输出全都是4.
    		defer func() { fmt.Println(i) }()
    	}
    }
    

    解决办法:

    package main
    
    import "fmt"
    
    func main() {
    	var whatever = [5]int{1,2,3,4,5}
    	for i,_ := range whatever {
    		i := i //将i进行重新赋值,每个i都开辟一个自己的内存空间
    		defer func() { fmt.Println(i) }()
    	}
    }
    

    二、异常处理 

    Go语言中使用 panic 抛出错误,recover 捕获错误。

    异常的使用场景简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。

    panic:

    1. 内置函数

    2. 假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行

    3. 返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行

    4. 直到goroutine整个退出,并报告错误

    recover:

    1. 内置函数

    2. 用来捕获panic,从而影响应用的行为

    golang 的错误处理流程:当一个函数在执行过程中出现了异常或遇到 panic(),正常语句就会立即终止,然后执行 defer 语句,再报告异常信息,最后退出 goroutine。

    如果在 defer 中使用了 recover() 函数,则会捕获错误信息,使该错误信息终止报告。

    注意:

    1. 利用recover处理panic指令,defer 必须放在 panic 之前定义,另外 recover 只有在 defer 调用的函数中才有效。否则当panic时,recover无法捕获到panic,无法防止panic扩散。

    2. recover 处理异常后,逻辑并不会恢复到 panic 那个点去,函数跑到 defer 之后的那个点。

    3. 多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用。

    package main
    
    func main() {
        test()
    }
    
    func test() {
        defer func() {
            if err := recover(); err != nil {
                println(err.(string)) // 将 interface{} 转型为具体类型。
            }
        }()
    
        panic("panic error!")
    }
    

    由于 panic、recover 参数类型为 interface{},因此可抛出任何类型对象。

     func panic(v interface{})
     func recover() interface{}
    

    延迟调用中引发的错误,可被后续延迟调用捕获,但仅最后一个错误可被捕获:

    package main
    
    import "fmt"
    
    func test() {
        defer func() {
            // defer panic 会打印
            fmt.Println(recover())
        }()
    
        defer func() {
            panic("defer panic")
        }()
    
        panic("test panic")
    }
    
    func main() {
        test()
    }
    

    如果需要保护代码段,可将代码块重构成匿名函数,如此可确保后续代码被执 :

    package main
    
    import "fmt"
    
    func test(x, y int) {
        var z int
    
        func() {
            defer func() {
                if recover() != nil {
                    z = 0
                }
            }()
            panic("test panic")
            z = x / y
            return
        }()
    
        fmt.Printf("x / y = %d\n", z)
    }
    
    func main() {
        test(2, 1)
    }
    

    除用 panic 引发中断性错误外,还可返回 error 类型错误对象来表示函数调用状态:

    type error interface {
        Error() string
    }
    

    标准库 errors.Newfmt.Errorf函数用于创建实现 error 接口的错误对象。通过判断错误对象实例来确定具体错误类型。

    package main
    
    import (
        "errors"
        "fmt"
    )
    
    var ErrDivByZero = errors.New("division by zero")
    
    func div(x, y int) (int, error) {
        if y == 0 {
            return 0, ErrDivByZero
        }
        return x / y, nil
    }
    
    func main() {
        defer func() {
            fmt.Println(recover())
        }()
        switch z, err := div(10, 0); err {
        case nil:
            println(z)
        case ErrDivByZero:
            panic(err)
        }
    }

    Go实现类似 try catch 的异常处理:

    package main
    
    import "fmt"
    
    func Try(fun func(), handler func(interface{})) {
    	defer func() {
    		if err := recover(); err != nil {
    			handler(err)
    		}
    	}()
    	fun()
    }
    
    func main() {
    	Try(func() {
    		panic("test panic")
    	}, func(err interface{}) {
    		fmt.Println(err)
    	})
    }
    

    如何区别使用 panic 和 error 两种方式?

    惯例是:导致关键流程出现不可修复性错误的使用 panic,其他使用 error。

    三、import

  • 相关阅读:
    阿里云 CDN+OSS 解决方案
    一次完整的HTTP请求过程
    apache多站点配置中ServerAlias什么意思
    legend3---apache配置https
    legend3---Fiddler如何抓手机app的包
    Fiddler:增加IP列
    http请求报文格式和响应报文格式
    http请求头中Referer的含义和作用
    Chrome保存的HAR文件怎么打开
    Android Studio 错误 Duplicate files copied in APK META-INF/LICENSE.txt
  • 原文地址:https://www.cnblogs.com/hero799/p/15954487.html
Copyright © 2020-2023  润新知