Go面试题专题(一):聊聊你理解的Golang defer关键字

2023-11-12

defer关键字是我们工作中经常用到的go语言特性,也是面试官比较青睐的一个知识点,今天通过这篇文章带各位道友彻底掌握它。

面试题文档下链接点击这里免积分下载

go语言入门到精通点击这里免积分下载

defer两大特性

defer是golang中的一个关键字,它主要具有两大特性:

- 延迟调用: 在当前函数执行完成后调用执行。

func f1(){
	defer fmt.Println("hello world")

	fmt.Println("hello defer!")
}

输出结果:

$ go run main.go
hello defer!
hello world

后进先出: 多个defer函数时,执行顺序为后进先出。

func f2(){
	defer fmt.Println("hello 1!")
	defer fmt.Println("hello 2!")
	defer fmt.Println("hello 3!")

	fmt.Println("hello defer!")
}

输出结果

$ go run main.go
hello defer!
hello 3!
hello 2!
hello 1!

defer与return的执行顺序

defer与return的执行顺序,是面试时经常考察的一点,需要道友们好好理解。

首先,我们举个栗子,看如下情况下代码的输出结果。

func f1() (r int){
	defer func(){
		r++
	}()
	return 0
}

func f2() (r int) {
	t:=5
	defer func() {
		t = t+5
	}()
	return t
}

func f3() (r int) {
	defer func(r int) {
		r = r+5
	}(r)
	return 0
}

func main(){
	fmt.Println(f1())
	fmt.Println(f2())
	fmt.Println(f3())
}

建议朋友们先思考下答案,再往后看。

$ go run main.go
1
5
0

张三道友心想:卧槽,开什么玩笑,难道不是 0、10、5…

好的,下面我们逐一分析一下:

这里我们需要先理解下return语句的执行顺序。

return语句本身并不是一条原子指令,它会先给返回值赋值,然后再是返回,如下

func f4() (r int) {
	return 1
}

//执行过程:
r:=1 //赋值
ret //执行返回

而在含defer表达式时,函数返回的过程是这样的:

先给返回值赋值,然后调用defer表达式,最后再是返回结果

即对于f1()来讲

func f1() (r int){
	defer func(){
		r++
	}()
	return 0
}

//执行过程:
r:=0 //赋值
r++  //defer
ret  //r=1

对于f2来讲

func f2() (r int) {
	t:=5
	defer func() {
		t = t+5
	}()
	return t
}

//执行过程
t:=5
r:=t
t=t+5 //defer
ret  //r=5

对于f3()来讲,在defer的时候传参r,其实是一个值拷贝。

所以defer中对r的修改并不会影响返回值结果,助于理解把r换成t,结果是等同的,即等效为

func f3() (r int) {
	defer func(t int) {
		t = t+5
	}(r)
	return 0
}

//执行过程
r:=0
t = r, t = t +5 //defer
ret // r=0

defer的应用场景

场景一:资源释放

我们在代码中使用资源时如:打开一个文件,很容易因为忘记释放或者由于逻辑上的错误导致资源没有关闭。这时候使用defer可以避免这种资源泄漏。不妨先看如下代码:

file,_ := os.Open("test.txt")
//process为业务逻辑处理
if err:=process(file);err!=nil {
  return
}
file.Close()

上面的代码即存在一个严重的问题,如 err!=nil 直接return后,会使得file.close() 关闭资源的语句没有执行,导致资源泄漏。
且在经历了一串业务逻辑处理编写后,我们也很容易忘记关闭资源导致资源泄漏。所以应该牢记一个原则:在每个资源申请成功的后面都加上defer自动清理,不管该函数都多少个return,资源都会被正确的释放。
正确的编写逻辑如下:

file,_ := os.Open("test.txt")
defer file.Close()
//process为业务逻辑处理
if err:=process(file);err!=nil {
  return
}

场景二:异常捕获

Golang中对于程序中的异常处理,没有try catch,但是有panic和recover。 当程序中抛出panic时,如果没有及时recover,会导致服务直接挂掉,造成很严重的后果,所以我们一般用recover来捕获异常。

func main(){
	defer func(){
		if ok:=recover();ok!=nil{
			fmt.Println("recover")
		}
  }()
	panic("error")
}

上面两个场景是我们必需要熟知的,当然还可以利用defer的特性优雅的实现一些类似于代码追踪、记录函数的参数和返回值等。

# 场景三: 代码追踪

我们通过追踪程序进入或离开某个函数的信息,来测试此函数是否被执行。

func main(){
	f1()
	f2()
}

func f1(){
	defer trace_leave(trace_enter("f1()"))
	fmt.Println("f1()程序逻辑")
}

func f2(){
	defer trace_leave(trace_enter("f2()"))
	fmt.Println("f2()程序逻辑")
}

func trace_enter(msg string) string{
	fmt.Println("enter: ",msg)
	return msg
}

func trace_leave(msg string) {
	fmt.Println("leave: ",msg)
}

输出结果如下:

$go run main.go
enter:  f1()
f1()程序逻辑
leave:  f1()
enter:  f2()
f2()程序逻辑
leave:  f2()

场景四: 打印函数的参数和返回值

某函数的执行结果不符合预期,我们可以使用defer来一步到位的打印函数的参数和返回值,而非多处打印调试语句。

func main(){
	func1("hello")
}

func func1(str string) ( res string) {
	defer func() {
		fmt.Printf("func1(%s) = %s", str, res)
	}()
	res = fmt.Sprintf("%s, jack!",str)
	return
}

输出结果:

$go run main.go
func1(hello) = hello, jack!

面试点总结

  • defer的两大特性
  • defer与return的执行顺序
  • defer的应用场景

后语

如果大家对本文提到的面试技术点有任何问题,都可以在评论区进行回复哈,我们共同学习,一起进步!

关注公众号[简道编程],每天一个后端技术面试点

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Go面试题专题(一):聊聊你理解的Golang defer关键字 的相关文章

随机推荐

  • 【Opencv&Cpp】12 像素统计:最大/小值、平均值、标准差

    minMaxLoc 找到全局最小和最大值 meanStdDev 计算矩阵的均值和标准偏差 找到全局最小和最大值 minmaxloc minMaxLoc InputArray src double minVal double maxVal 0
  • 大家来讨论怎么写概要设计

    http blog csdn net sunwill chen article details 7864904 笔者声明 本文讲述笔者浅薄的观点 意在抛砖引玉 望网友一起发表观点共同切磋 目前网络上的概要设计格式繁多 质量也是参差不齐 许多
  • 单链表C语言代码实现

    一 代码 include
  • sqli-labs-master靶场搭建以及报错解决

    一 前提准备 1 下载 sqli labs master mirrors audi 1 sqli labs GitCode 2 安装PHP study Windows版phpstudy下载 小皮面板 phpstudy xp cn 二 搭建靶
  • 华为OD机试 - 最小传输时延(Java)

    题目描述 某通信网络中有N个网络结点 用1到N进行标识 网络通过一个有向无环图表示 其中图的边的值表示结点之间的消息传递时延 现给定相连节点之间的时延列表times i u v w 其中u表示源结点 v表示目的结点 w表示u和v之间的消息传
  • Pycharm中修改注释文本的颜色(详细设置步骤)

    下面是在Pycharm中设置注释文本颜色的详细步骤 下面是修改前后对比 修改前注释行的颜色 修改后注释行的颜色 以上就是Pycharm中修改注释文本颜色的详细步骤 希望能帮到你
  • 小程序真机调试连接本地服务器进行调试

    小程序连接本地服务器 开发小程序时经常会遇到需要连接本地服务器进行调试的时候 但是总是连接不上 这里就说一下本菜鸟连接本地服务器的方法 第一步 把下图红框的地方勾选住 好多方法都得选这一步 第二步 设置里面代理按图中勾选 第三步是连接的方法
  • JavaScript避免使用return跳出多重循环从而继续执行函数;使用break跳出多重for循环

    一 先来看一下使用break仅跳出一层for循环的用法 const foo function for let i 1 i lt 3 i for let j 1 j lt 3 j if i 2 break console log 输出j的值
  • Mac上使用GPU加速训练模型

    文章目录 前言 使用GPU 前言 上一篇文章中我介绍了使用pytorch的一个完整模型训练套路 其中没有使用gpu 如果要使用gpu的话 win上我们可以使用cuda mac上可以使用mps 而我自己是mac电脑 需要进行如下修改 使用GP
  • ubuntu下docker配置国内镜像源

    既然都看到这篇文章了 就不解释为什么需要配置国内镜像源了 直接上步骤 此文使用ubuntu环境为Ubuntu 18 04 4 LTS 使用 sudo vim etc docker daemon json 命令新建或编辑文件 输入以下内容 r
  • 大话设计模式C++实现-第23章-命令模式

    一 UML图 二 概念 命令模式 Command 将一个请求封装为一个对象 从而使你可用不同的请求对客户进行参数化 对请求进行排队或记录请求日志 以及支持可撤销的操作 三 说明 角色 1 Command类 用来声明执行操作的接口 2 Con
  • 漫谈 SLAM 技术(下)

    转自 https zhuanlan zhihu com p 135958593 3 视觉SLAM系统关键问题 结合上述介绍的SLAM系统 我们从以下几个方面分析视觉SLAM系统需要考虑的关键问题 1 图像信息使用 视觉SLAM方法根据使用图
  • 模块""可能与您正在运行的Windows版本不兼容。检查该模块是否与regsvr32.exe的x86或x64版

    本人最近在研究mencoder 转换视频格式 发现转换rmvb需要 1 把drv43260 dll拷贝到系统的system32文件夹下 2 开始 gt 运行 gt regsvr32 drv43260 dll 来自 http topic cs
  • C++容器之 vector map set查找元素

    前面两篇基本上讲解容器的增加删除 其实现实世界中对数据的查找才是最大的需求 下面主要围绕着容器的查找来讲解 首先 由于vector没有实现find 方法 只能使用algorithm提供的find 方法 所以 直接在vector查找节介绍al
  • 多线程编程

    Linux线程概述 内核线程和用户线程 线程是程序中完成独立任务的完整执行序列 即一个可调度的实体 根据运行环境和调度者身份 线程分为内核线程和用户线程 内核线程 在有的系统上也称为LWP 轻量级进程 运行在内核空间 由内核调度 用户线程
  • 概率在计算机学中的应用,概率统计在计算机中的应用

    概率统计在计算机中的应用 一 综述 研究自然界中随机现象统计规律的数学方法 叫做概率统计 又称数理统计方法 概率论 是根据大量同类随机现象的统计规律 对随机现象出现某一结果的可能性作出一种客观的科学判断 对这种出现的可能性大小做出数量上的描
  • 交换机ACL配置

    交换机ACL配置 实验要求 PC3能ping通R3和R4 PC2能ping通R3和R4 R3和R4全网互通 通过ACL使PC2不能ping通PC3 先给每个接口配置ip PC3 192 168 5 3 24 192 168 5 254 PC
  • ngx_http_ssl_module

    ngx http ssl module 模块 语法 ssl 在 从 默认值 ssl off 背景 http 服务器 支持HTTPS协议为给定的虚拟服务器 推荐使用 ssl 参数的 听 指令而不是 这个指令 语法 ssl buffer siz
  • 哈哈,太真实了!除了《颈椎康复指南》,还有这 9 本书

    点击上方 Java后端 选择 设为星标 优质文章 及时送达 作者 sivagao 链接 https github com sivagao 本文罗列的这些书籍封面其实是各种典型的反模式 不过它们真的是非常常见以至于大家都习以为常了 从 Sta
  • Go面试题专题(一):聊聊你理解的Golang defer关键字

    defer关键字是我们工作中经常用到的go语言特性 也是面试官比较青睐的一个知识点 今天通过这篇文章带各位道友彻底掌握它 面试题文档下链接点击这里免积分下载 go语言入门到精通点击这里免积分下载 文章目录 defer两大特性 defer与r