defer ,panic,recover

栏目: IT技术 · 发布时间: 4年前

内容简介:多个defer出现的时候,它是一个“栈”的关系,也就是先进后出。一个函数中,写在前面的defer会比写在后面的defer调用的晚。return之后的语句先执行,defer后的语句后执行。说明:defer,return 和返回值三者之间的执行逻辑

defer的执行顺序

多个defer出现的时候,它是一个“栈”的关系,也就是先进后出。一个函数中,写在前面的defer会比写在后面的defer调用的晚。

package main

import "fmt"

func main() {
    defer func1()
    defer func2()
    defer func3()
}

func func1() {
    fmt.Println("A")
}

func func2() {
    fmt.Println("B")
}

func func3() {
    fmt.Println("C")
}
//输出结果:C B A

defer和return的先后顺序

return之后的语句先执行,defer后的语句后执行。

package main

import "fmt"

func deferFunc() int {
    fmt.Println("defer func called")
    return 0
}

func returnFunc() int {
    fmt.Println("return func called")
    return 0
}

func returnAndDefer() int {

    defer deferFunc()

    return returnFunc()
}

func main() {
    returnAndDefer()
}
//输出
//return func called
//defer func called

defer 无名返回值 有名返回值

  • 函数返回值初始化

    该知识点不属于defer本身,但是调用的场景却与defer有联系,所以也算是defer必备了解的知识点之一。

    如 : func DeferFunc1(i int) (t int) {}

    其中返回值t int,这个t会在函数起始处被初始化为对应类型的零值并且作用域为整个函数。

package main

import "fmt"

func DeferFunc(i int) (t int) {

    fmt.Println("t = ", t)

    return 2
}

func main() {
    DeferFunc(10)
}
//输出
//t =  0
  • defer、return 和无名的返回值
package main

func main() {
    name := run()
    println("return: name = " + name) 
}

func run() (string) {
    name := "Paul"
    defer sayHello(&name)
    name = "John"
    return name
}

func sayHello(name *string) {
    *name = "George"
    println("Hello " + *name)
}
// 输出
// Hello George
// return: name = John
  • defer、return 和有名返回值
//传指针
package main

func main() {
    name := run()
    println("return: name = " + name)
}

func run() (x string) {
    name := "Paul"
    x = name
    defer sayHello(&x)
    name = "John"
    return name
}

func sayHello(name *string) {
    println("Hello " + *name)
    *name = "George"
    println("Hello " + *name)
}
//输出
//Hello John
//Hello George
//return: name = George

//传值
package main

func main() {
    name := run()
    println("return: name = " + name)
}

func run() (x string) {
    name := "Paul"
    x = name
    defer sayHello(x)
    name = "John"
    return name
}

func sayHello(name string) {
    println("Hello " + name)
    name = "George"
    println("Hello " + name)
}

//输出
//Hello Paul  
//Hello George 
//return: name = John

说明:defer,return 和返回值三者之间的执行逻辑

return最先执行,return负责将结果写入返回值中;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出

defer 闭包

  • 闭包是什么

    闭包是由函数及其相关引用环境组合而成的实体,即

    闭包=函数+引用环境

一般的函数都有函数名,但是匿名函数就没有。匿名函数不能独立存在,但可以直接调用或者赋值于某个变量。匿名函数也被称为闭包,一个闭包继承了函数声明时的作用域。在Golang中,所有的匿名函数都是闭包。

有个不太恰当的例子,可以把闭包看成是一个类,一个闭包函数调用就是实例化一个类。闭包在运行时可以有多个实例,它会将同一个作用域里的变量和常量捕获下来,无论闭包在什么地方被调用(实例化)时,都可以使用这些变量和常量。而且,闭包捕获的变量和常量是引用传递,不是值传递。

package main

func main() {
    name := run()
    println("return: name = " + name)
}

func run() (string) {
    name := "Paul"
    aFun := func() {
        println("Hello " + name)
        name = "George"
        println("Hello " + name)
    }
    name = "John"
    aFun()
    bfunc:= func() {
        println("Hello " + name)
        name = "Bobbi"
        println("Hello " + name)
    }
    bfunc()
    return name
}
//输出
//Hello John
//Hello George
//Hello George
//Hello Bobbi
//return: name = Bobbi
  • defer与闭包
package main

func main() {
    name := run()
    println("return: name = " + name)
}

func run() (x string) {
    name := "Paul"
    aFun := func() {
        println("Hello " + x)
        x = "George"
        println("Hello " + x)
    }
    defer aFun()
    name = "John"
    return name
}
//输出
//Hello John
//Hello George
//return: name = George




package main

func main() {
    name := run()
    println("return: name = " + name)
}

func run() (x string) {
    name := "Paul"
    x = name
    aFun := func(x string) {
        println("Hello " + x)
        x = "George"
        println("Hello " + x)
    }
    defer aFun(x)
    name = "John"
    return name
}
//输出:
//Hello Paul
//Hello George
//return: name = John

defer panic recover

能够触发defer的是遇见return(或函数体到末尾)和遇见panic。

遇见panic的情况:遍历本协程的defer链表,并执行defer。在执行defer过程中:遇到recover则停止panic,返回recover处继续往下执行。如果没有遇到recover,遍历完本协程的defer链表后,向stderr抛出panic信息。

A. defer遇见panic,但是并不捕获异常的情况

package main

import (
    "os"
    "fmt"
    "time"
)

func main() {
    var user = os.Getenv("USER_")
    go func() {
        defer func() {
            fmt.Println("defer here")
        }()

        if user == "" {
            panic("should set user env.")
        }
    }()

    time.Sleep(1 * time.Second)
    fmt.Printf("get result %d\r\n", 1)
}
//输出
//defer here
//panic: should set user env.

说明:会发现defer中的字符串”defer here”打印出来了,而main流程中的”ger result”没有打印,说明panic坚守了自己的原则:

执行,且只执行,当前goroutine的defer

B.defer遇见panic,并捕获异常

package main

import (
    "fmt"
)

func main() {
    defer_call()

    fmt.Println("main 正常结束")
}

func defer_call() {

    defer func() {
        fmt.Println("defer: panic 之前1, 捕获异常")
        if err := recover(); err != nil {
            fmt.Println(err)
        }
    }()

    defer func() { fmt.Println("defer: panic 之前2, 不捕获") }()

    panic("异常内容") //触发defer出栈

    defer func() { fmt.Println("defer: panic 之后, 永远执行不到") }()
}

//输出
//defer: panic 之前2, 不捕获
//defer: panic 之前1, 捕获异常
//异常内容
//main 正常结束

欢迎关注我们的微信公众号,每天学习 Go 知识

defer ,panic,recover

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Rapid Web Applications with TurboGears

Rapid Web Applications with TurboGears

Mark Ramm、Kevin Dangoor、Gigi Sayfan / Prentice Hall PTR / 2006-11-07 / USD 44.99

"Dear PHP, It's over between us. You can keep the kitchen sink, but I want my MVC. With TurboGears, I was able to shed the most heinous FileMaker Pro legacy 'solu-tion' imaginable. It has relationshi......一起来看看 《Rapid Web Applications with TurboGears》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具