Go面试题:聊聊你理解的Golang defer关键字

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

文章目录

  • defer两大特性
  • defer与return的执行顺序
  • defer的应用场景
  • 面试点总结

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:=5defer 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

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

这里我们需要先理解下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:=5defer 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的应用场景

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/181328.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【代码】微电网两阶段鲁棒优化经济调度方法(完美复现)matlab-yalmip-cplex/gurobi

程序名称:两阶段鲁棒优化—微电网两阶段鲁棒优化经济调度方法_刘一欣 实现平台:matlab-yalmip-cplex/gurobi 简介:针对微电网内可再生能源和负荷的不确定性,建立了 min-max-min 结构的两阶段鲁棒优化模型,可得到最恶…

双向队列:LeetCode1670 设计前中后队列

1670. 设计前中后队列 请你设计一个队列,支持在前,中,后三个位置的 push 和 pop 操作。 请你完成 FrontMiddleBack 类: FrontMiddleBack() 初始化队列。void pushFront(int val) 将 val 添加到队列的 最前面 。void pushMiddle…

课时7作业

Description 假如有n个台阶,一次只能上1个台阶或2个台阶,请问走到第n个台阶有几种走法?为便于读者理解题意,这里举例说明如下:假如有3个台阶,那么总计就有3种走法:第一种为每次上1个台阶&#…

DS八大排序之直接插入排序和希尔排序

前言 我们前面几期介绍了线性和非线性的基本数据结构。例如顺序表、链表、栈和队列、二叉树等~!本期和接下来的几期我们来详解介绍各个排序的概念、实现以及性能分析! 本期内容 排序的概念以及其运用 常见的排序算法 直接插入排序 希尔排序 一、排序的…

“SRP模型+”多技术融合在生态环境脆弱性评价模型构建、时空格局演变分析与RSEI 指数的生态质量评价及拓展

近年来,国内外学者在生态系统的敏感性、适应能力和潜在影响等方面开展了大量的生态脆弱性研究,他们普遍将生态脆弱性概念与农牧交错带、喀斯特地区、黄土高原区、流域、城市等相结合,评价不同类型研究区的生态脆弱特征,其研究内容…

Jmeter性能综合实战——签到及批量签到

提取性能测试的三个方面:核心、高频、基础功能 签 到 请 求 步 骤 1、准备工作: 签到线程组 n HTTP请求默认值 n HTTP cookie 管理器 n 首页访问请求 n 登录请求 n 查看结果树 n 调试取样器 l HTTP代理服务器 (1)创建线…

12:kotlin类和对象 -- 继承

kotlin中的所有类都有一个共同的超类Any class Example // 没有声明,但是默认实现Any类Any有三个方法:equals()、hashCode()和toString()。 默认情况下,kotlin类是final修饰的,所以它们不能被继承。要使一个类可继承,用open关键…

vue建立组件无校验版

实现功能&#xff1a; 切换&#xff0c;相当于tab 1、非组件代码&#xff1a; <template><div><div class"tabStyle"><div v-for"(item,index) in tabTitle" :key"index" class"bordItemStyle" :class"c…

Spring框架体系及Spring IOC思想

目录 Spring简介Spring体系结构SpringIOC控制反转思想自定义对象容器Spring实现IOCSpring容器类型容器接口容器实现类对象的创建方式使用构造方法使用工厂类的方法使用工厂类的静态方法对象的创建策略对象的销毁时机生命周期方法获取Bean对象的方式通过id/name获取通过类型获取…

Kvm 管理器突然打不开 启动虚拟系统管理器出错: g-dbus-error-quark: GDBus.Error:org.freed

环境&#xff1a; Ubuntu20.04 KVM mobaxterm 11.1 问题描述&#xff1a; 启动虚拟系统管理器出错: g-dbus-error-quark: GDBus.Error:org.freedesktop.DBus.Error.NoReply: Message recipient disconnected from message bus without replying (4)Traceback (most recen…

一文搞定XMLHttpRequest,AJAX,Promise,Axios及操作实战

XMLHttpRequest,AJAX,Promise,Axios都是发送异步请求的工具&#xff0c;只是使用的场合和方式有所不同。都是一种用于创建快速动态网页的技术 1、认识同步请求和异步请求&#xff1f; 1.1、认识同步请求&#xff1f; 1、我们点击淘宝首页的男装&#xff0c;进入男装页面的这…

C++中的模板

模板概论 c提供了函数模板 (function template.) 所谓函数模板&#xff0c;实际上是建立一个通用函 数&#xff0c;其函数类型和形参类型不具体制定&#xff0c;用一个虚拟的类型来代表。这个通用函数 就成为函数模板。凡是函数体相同的函数都可以用这个模板代替&#xff0c;不…

好物分享(领劵优惠好手)

本次分享一个领取领优惠、充值、会员、购物优惠等功能 部分截图 需要的点击下方按钮 前往体验 感谢大家的支持 更多内容请关注微信小程序&#xff0c;源码、插件、模板及时更新

TypeScript 之 Date日期对象

online工具&#xff1a; PlayGround 官方源码&#xff1a; GitHub TypeScript Date 在JavaScript或TypeScript中&#xff0c;使用 Date对象可用于获取日期和时间等。 let date new Date(); console.log(typeof(date)); // "object" console.log(date); …

【C++干货铺】STL简述 | string类的使用指南

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 什么是STL STL的版本 STL的六大组件 STL的缺陷 string类 C语言中的字符串 标准库中的string类 string类常用的接口使用指南 string类中常见的构造 strin…

CUDA安装及环境配置——最新详细版

确定安装版本 在安装之前呢&#xff0c;我们需要确定三件事 第一&#xff1a;查看显卡支持的最高CUDA的版本&#xff0c;以便下载对应的CUDA安装包 第二&#xff1a;查看对应CUDA对应的VS版本&#xff0c;以便下载并安装对应的VS版本&#xff08;vs需要先安装&#xff09; 第三…

【vue脚手架配置代理+github用户搜索案例+vue项目中常用的发送Ajax请求的库+slot插槽】

vue脚手架配置代理github用户搜索案例vue项目中常用的发送Ajax请求的库slot插槽 1 vue脚手架配置代理2 github用户搜索案例2.1 静态列表2.2 列表展示2.3 完善案例 3 vue项目中常用的发送Ajax请求的库3.1 xhr3.2 jQuery3.3 axios3.4 fetch3.5 vue-resource 4 slot 插槽4.1 效果4…

【沁恒蓝牙mesh】CH58x 将RTC时钟切换为LSE外部低速时钟

本文主要记录了【沁恒蓝牙mesh】CH58x 如何将RTC时钟切换为外部时钟 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是喜欢记录零碎知识点的小菜鸟。&#x1f60e;&#x1f4dd; 个人主页&#xff1a;欢迎访问我的 Ethernet_Comm 博客主页&#x1f525;&#x1f389;…

实测有效的 8 个顶级Android 数据恢复工具

由于我们现在生活在一个依赖数字数据的时代&#xff0c;当重要文件从我们的 Android 手机中消失时&#xff0c;这将是一场数字噩梦。如果您没有预先备份Android手机上的数据或未能通过备份找到已删除的数据&#xff0c;那么选择最好的Android数据恢复软件是最佳选择。 因此&am…

【C 语言经典100例】C 练习实例13 - 水仙花数

题目&#xff1a;打印出所有的"水仙花数"&#xff0c;所谓"水仙花数"是指一个三位数&#xff0c;其各位数字立方和等于该数 本身。例如&#xff1a;153是一个"水仙花数"&#xff0c;因为1531的三次方&#xff0b;5的三次方&#xff0b;3的三次方…