1:后定义的defer先执行(可以理解为栈的方式)

// 222
// 111
func Test1(t *testing.T) {
defer fmt.Println("111")
defer fmt.Println("222")
}

2:defer定义的函数的参数,在定义的时候已经被赋值了

// defer 函数定义的时候,参数已经被赋值了(参数被捕捉了)

// 输出:
// 第二个defer: 2
// 第一个defer: 1
func Test2(t *testing.T) {
a := 1 // 注意这里,a作为defer函数的参数。defer函数定义的时候,就把a的值确定了
defer fmt.Println("第一个defer: ", a) defer func() {
fmt.Println("第二个defer: ", a)
}() a+=1
}

3:defer函数可以修改具名返回值,但不会修改匿名返回值

/////////////////////////////////////////////////////////
// defer 函数可以修改具名返回值,但是不会修改匿名返回值
// 输出
// fHasName: 2
// fNoName: 1
func fHasName() (val int) {
val = 1
defer func() {
// 因为val是个具名返回值,所以这里对val的操作,会影响到返回值
val += 1
}() return val
}
func fNoName() int {
val := 1
defer func() {
// 这里的操作则不会影响返回值
val += 1
}() return val
}

4:遇到panic的时候,当前协程调用栈已定义的defer将被执行,如果当前协程没有任何一个defer函数内执行了recover,那么执行完所有的defer之后,将会触发panic退出进程

/* 输出
has: 333
err: haha
has: 222
has: 111
*/
func TestHasRecover(t *testing.T) {
defer fmt.Println("has: 111")
defer fmt.Println("has: 222")
defer func() {
if err := recover(); err != nil {
fmt.Println("err:", err)
}
}()
defer fmt.Println("has: 333")
panic("haha")
defer fmt.Println("has: 444")
} /* 输出
has: 333
has: 222
has: 111 panic: haha [recovered]
panic: haha goroutine 19 [running]:
testing.tRunner.func1.1(0x95df20, 0x9b2110)
C:/Go/src/testing/testing.go:1076 +0x310
testing.tRunner.func1(0xc000140480)
C:/Go/src/testing/testing.go:1079 +0x43a
*/
func TestNoRecover(t *testing.T) {
defer fmt.Println("has: 111")
defer fmt.Println("has: 222")
defer fmt.Println("has: 333")
panic("haha")
defer fmt.Println("has: 444")
}

最后来一道总结题,如下代码会输出什么?


import "testing"
import "fmt" func sum(tip string, a int, b int) int {
sum := a + b
fmt.Printf("tip=%s a=%d b=%d sum=%d\n", tip, a, b, sum)
return sum
} func deferSum() {
a := 1
b := 2
defer sum("1tip", a, sum("2tip", a, b))
a = 3
defer func(b int) {
sum("3tip", a, sum("4tip", a, b))
}(a)
b = 4
defer func() {
sum("5tip", a, sum("6tip", a, b))
}()
} func TestDeferSum(t *testing.T) {
deferSum()
}

正确答案如下:

tip=6tip a=3 b=4 sum=7
tip=5tip a=3 b=7 sum=10
tip=4tip a=3 b=3 sum=6
tip=3tip a=3 b=6 sum=9
tip=1tip a=1 b=3 sum=4

go特性-defer的更多相关文章

  1. golang学习 ---defer语句

    golang语言defer特性详解 defer语句是go语言提供的一种用于注册延迟调用的机制,它可以让函数在当前函数执行完毕后执行,是go语言中一种很有用的特性.由于它使用起来简单又方便,所以深得go ...

  2. Python也可以拥有延迟函数

    延迟函数defer 我们知道在Golang中有一个关键字defer,用它来声明在函数调用前,会让函数*延迟**到外部函数退出时再执行,注意,这里的退出含义:函数return返回或者函数panic退出 ...

  3. 浏览器环境下JavaScript脚本加载与执行探析之defer与async特性

    defer和async特性相信是很多JavaScript开发者"熟悉而又不熟悉"的两个特性,从字面上来看,二者的功能很好理解,分别是"延迟脚本"和"异 ...

  4. Go defer 特性和使用场景

    golang 的 defer 语句用于延迟调用.defer 会在当前函数返回之前执行 defer 注册的函数.比如 defer func_defer() 这样语句会让你注册一个函数变量到 defer ...

  5. JS魔法堂:ES6新特性——GeneratorFunction介绍

    一.前言       第一次看koajs的示例时,发现该语句 function *(next){...............} ,这是啥啊?于是搜索一下,原来这是就是ES6的新特性Generator ...

  6. 【Go入门教程3】流程(if、goto、for、switch)和函数(多个返回值、变参、传值与传指针、defer、函数作为值/类型、Panic和Recover、main函数和init函数、import)

    这小节我们要介绍Go里面的流程控制以及函数操作. 流程控制 流程控制在编程语言中是最伟大的发明了,因为有了它,你可以通过很简单的流程描述来表达很复杂的逻辑.Go中流程控制分三大类:条件判断,循环控制和 ...

  7. iOS - Swift Swift 语言新特性

    1.Swift 2.0 带来哪些新变化 常规变化: 1.OS X 10.11.iOS 9 和 watchOS 2 SDK 采纳了一些 Objective-C 的特性用来提高 Swift 的编程体验, ...

  8. HTML5 <script>元素async,defer异步加载

    原文地址:HTML5′s async Script Attribute原文日期: 2010年09月22日翻译日期: 2013年08月22日 (译者注: 异步加载,可以理解为无阻塞并发处理.) (译者再 ...

  9. javascript笔记——js的阻塞特性[转载]

    JS具有阻塞特性,当浏览器在执行js代码时,不能同时做其它事情,即<script>每次出现都会让页面等待脚本的解析和执行(不论JS是内嵌的还是外链的),JS代码执行完成后,才继续渲染页面. ...

随机推荐

  1. vue-cli中使用全局less变量

    1.执行 vue add style-resources-loader 命令,选择less 2.在 vue.config.js中添加配置,注意将路径更改为自己存放less变量文件的地址 // 全局使用 ...

  2. kubelet拉取pause镜像报错pull access denied for 172.20.59.190:81/kubernetes/pause-amd64, repository does not exist or may require 'docker login': denied

    目录 1 背景说明 2 现象 pod无法启动,一直显示ContainerCreating 3 问题分析 kubelet的启动参数如下 4 尝试的解决方法 4.1 本地docker login登录镜像仓 ...

  3. Linux 系统编程 学习:02-进程间通信1:Unix IPC(1)管道

    Linux 系统编程 学习:02-进程间通信1:Unix IPC(1)管道 背景 上一讲我们介绍了创建子进程的方式.我们都知道,创建子进程是为了与父进程协作(或者是为了执行新的程序,参考 Linux ...

  4. Linux 网络编程的5种IO模型:信号驱动IO模型

    Linux 网络编程的5种IO模型:信号驱动IO模型 背景 上一讲 Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 我们讲解了多路复用等方面的知识,以及有关例程. ...

  5. Charles使用part2——代理设置

    一.charles代理原理: 如果本地开了代理:  二.设置代理 1.设置代理端口: proxy->proxy setting 打开代理设置界面,代理端口默认是 8888,可以使用默认也可以自己 ...

  6. adb命令如何获取appPackage和appActivity的信息

    如何获取appPackage和appActivity的信息,这里有一个极为实用的命令:adb shell dumpsys activity |find "mFocusedActivity&q ...

  7. drf JWT认证模块与自定制

    JWT模块 在djangorestframework中,有一款扩展模块可用于做JWT认证,使用如下命令进行安装: pip install djangorestframework-jwt 现在,就让我们 ...

  8. K8S 搭建 Kafka:2.13-2.6.0 和 Zookeeper:3.6.2 集群

    搭建 Kafka:2.13-2.6.0 和 Zookeeper:3.6.2 集群 一.服务版本信息: Kafka:v2.13-2.6.0 Zookeeper:v3.6.2 Kubernetes:v1. ...

  9. STM32CubeIDE下载安装-GPIO基本配置操作-Debug调试(基于CMSIS DAP Debug)

    1.在ST官网下载STM32CubeIDE而不是STM32CubeMX,并且STM32CubeIDE是免费的.(STM32CubeIDE不支持中文路径,不然编译会出错) 2.如果你用的是keil开发环 ...

  10. Java_Math类和Random类

    Math类 java.lang.Math提供了一系列静态方法用于科学计算, 其方法的参数和返回值类型一般都为double型, 如果需要更加强大的数学运算能力计算高等数学中的相关内容, 可使用apach ...