golang 详解defer

什么是defer

defer用来声明一个延迟函数,把这个函数放入到一个栈上, 当外部的包含方法return之前,返回参数到调用方法之前调用,也可以说是运行到最外层方法体的"}"时调用。我们经常用他来做一些资源的释放,比如关闭io操作

func doSomething(fileName string) {
    file,err := os.Open(fileName)
    if err != nil {
    panic(err)
    }
    defer file.Close()
}

defer 可以保证方法可以在外围函数返回之前调用。有点像其他言的 try finally

try{
}finally{
}

defer 读写外部变量

  defer声明的函数读写外部变量,和闭包差不多。比如下面的代码

func doSomething() {
    v := 10
    defer func() {
        fmt.Println(v)
        v++
        fmt.Println(v)
    }()
    v += 5
}

输出为

15
16

  就像闭包一样,如果不是defer函数方法内的变量会向上一层函数访问变量,重新做计算。

defer 读写命名的返回值

    这个例子中,defer声明的方法,给命名的返回值自增1

1 func doSomething() (rev int) {
2     defer func() {
3         rev++
4     }()
5 
6     return 5
7 }

  第6行的return 相当于

return rev = 5

  defer 声明的匿名函数会在return 之前执行,相当于

rev = 5
// 执行defer方法
rev++
//然后return
return

  所以结果是6

  我把代码做一点点修改

1 func doSomething() (rev int) {
2     v := 10
3     defer func() {
4         v++
5     }()
6 
7     return v
8 }

  第7行返回的是局部变量v.   

return v 相当于 return rev = v

  defer 函数里是对局部变量v的操作,所以与返回的rev没有关系。所有执行的结果是:10

defer 执行顺序

当有多个defer时执行顺序逆向的,后进先出:

func doSomething() {
    defer fmt.Println(1)
    defer fmt.Println(2)
}

会先输出2,再输出1

 defer 处理异常

  panic抛出异常后,如果不处理应用程序会崩溃。为了防止程序崩溃,我们可以在defer的函数里使用recover来捕获中异常:

func doSomething() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Print(err)
        }
        
    }()

    fmt.Println("Running...")
    panic("run error")
}

输出:

Running...
run error

recover 会捕获panic的异常。我再把代码做一点点修改:

func doSomething() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Print(err)
        }
        
    }()

    defer func() {
        panic("defer error")
    }()

    fmt.Println("Running...")
    panic("run error")
}

输出结果

Running...
defer error

因为 recover()只捕获最后一次panic

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据结构与算法

P3369 【模板】普通平衡树(Treap/SBT)(pb_ds版)

题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 插入x数 删除x数(若有多个相同的数,因只删除一个) 查询...

2517
来自专栏从零开始学 Web 前端

C/C++练习题(三)

分析:在我们看来,虽然使用字符数组和字符指针差不多,printf都可以打印出字符串出来,但是编译器对他们的处理完全不同。 对于字符指针,编译器看到后,...

1704
来自专栏开发与安全

《linux c 编程一站式学习》课后部分习题解答

1、假设变量x和n是两个正整数,我们知道x/n这个表达式的结果要取Floor,例如x是17,n是4,则结果是4。如果希望结果取Ceiling应该怎么写表达式呢?...

2736
来自专栏章鱼的慢慢技术路

Go指南练习_斐波纳契闭包

1283
来自专栏专注 Java 基础分享

Java 的字节流文件读取(二)

1291
来自专栏AzMark

Python 学习之元组列表

794
来自专栏微信公众号:Java团长

Java字节流与字符流的区别

字节流与和字符流的使用非常相似,两者除了操作代码上的不同之外,是否还有其他的不同呢? 实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的,而...

381
来自专栏技术之路

golang 详解defer

什么是defer defer用来声明一个延迟函数,把这个函数放入到一个栈上, 当外部的包含方法return之前,返回参数到调用方法之前调用,也可以说是运行到最外...

2647
来自专栏微信公众号:Java团长

Java虚拟机类加载机制

看到这个题目,很多人会觉得我写我的java代码,至于类,JVM爱怎么加载就怎么加载,博主有很长一段时间也是这么认为的。随着编程经验的日积月累,越来越感觉到了解虚...

832
来自专栏Java帮帮-微信公众号-技术文章全总结

Java面试系列10

Java面试系列10 1 Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别? Set里的元素是不能重...

2667

扫码关注云+社区