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

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

如有侵权,请联系 zhuanlan_guanli@qq.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏www.96php.cn

PHP编程效率的20个要点

用单引号代替双引号来包含字符串,这样做会更快一些。因为PHP会在双引号包围的字符串中搜寻变量,单引号则 不会,注意:只有echo能这么做,它是一种可以把多个字...

2609
来自专栏python百例

07-列表基础

311
来自专栏软件开发 -- 分享 互助 成长

谈谈 char *num="123";和char num[4]="123";的区别

最近写程序的时候发现这样一个问题 #include<iostream> #include <string.h> using namespace std; vo...

1718
来自专栏LEo的网络日志

python技巧分享(十)

40013
来自专栏我爱编程

Python DEBUG

>>> l = [3] >>> l() Traceback (most recent call last): File "<pyshell#77>", li...

2614
来自专栏Python小屋

Python使用pandas扩展库DataFrame对象的pivot方法对数据进行透视转换

Python扩展库pandas的DataFrame对象的pivot()方法可以对数据进行行列互换,或者进行透视转换,在有些场合下分析数据时非常方便。 DataF...

2934
来自专栏技术之路

golang 详解defer

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

2617
来自专栏java一日一条

详解Java中的clone方法:原型模式

clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象。所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创...

31
来自专栏软件开发 -- 分享 互助 成长

常量字符串和指针

为了节省内存,C++把常量字符串单独放在一个内存区域,如果有几个指针指向相同的常量字符串时,它们实际上指向的是相同的内存地址。 而数组是要每一个数组单独占用一块...

1825
来自专栏决胜机器学习

《Redis设计与实现》读书笔记(七) ——Redis对象综述及字符串对象实现原理

《Redis设计与实现》读书笔记(七) ——Redis对象综述及字符串对象实现原理 (原创内容,转载请注明来源,谢谢) 一、概述 redis不是直接的利用简单动...

2878

扫描关注云+社区