响应式网站区别,wordpress更换图片地址,网站注销申请,wordpress新闻模版面试题文档下链接点击这里免积分下载 文章目录 defer两大特性defer与return的执行顺序defer的应用场景面试点总结 defer两大特性
defer是golang中的一个关键字#xff0c;它主要具有两大特性#xff1a;
延迟调用: 在当前函数执行完成后调用执行。
func f1(){defer fmt.P…面试题文档下链接点击这里免积分下载 文章目录 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 t5}()return t
}func f3() (r int) {defer func(r int) {r r5}(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 //r1
对于f2来讲
func f2() (r int) {t:5defer func() {t t5}()return t
}//执行过程
t:5
r:t
tt5 //defer
ret //r5
对于f3()来讲在defer的时候传参r其实是一个值拷贝。
所以defer中对r的修改并不会影响返回值结果助于理解把r换成t结果是等同的即等效为
func f3() (r int) {defer func(t int) {t t5}(r)return 0
}//执行过程
r:0
t r t t 5 //defer
ret // r0
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的应用场景