以前讲过golang 的基本语法。但是,只是讲了一些基础的语法,感兴趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.html,前段时间有人问我defer,recover的用法。所以,还是统一的总结一下相关的关键字吧。

其实,Go语言是不支持 try…catch…finally 这种异常处理的,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,甚至一个小小的错误都抛出一个异常。
在Go语言中,使用多值返回来返回错误。不要用异常代替错误,更不要用来控制流程。在极个别的情况下,才使用Go中引入的Exception处理:defer, panic, recover。
 

一. defer 用法

defer的特性: 在函数返回之前, 调用defer函数的操作, 简化函数的清理工作.

在初接触到go时, 就被defer吸引住了,但是在使用defer关键字的时候,还是得注意这些:

1. 在defer表达式确定的时候,defer修饰的函数(后面统称为defered函数)的参数也就确定了

package main

import (
"fmt"
) func main() {
g()
} func g() {
i := 0
defer fmt.Println(i)
i++
return
}
-------output-------
0

2. 函数内可以有多个defered函数,但是这些defered函数在函数返回时遵守后进先出的原则

package main
import "fmt"

func main() {
g()
} func g() {
for i := 0; i<4; i++ {
defer fmt.Println(i)
}
}
-------output-------
3
2
1
0

3. 函数命名的返回值跟defered函数一起使用
函数的返回值有可能被defer更改,本质原因是return xxx语句并不是一条原子指令,执行过程是: 保存返回值(若有)-->执行defer(若有)-->执行return跳转。

func f() (result int) {
defer func() {
result++
}()
return
} func g() (r int) {
t :=
defer func() {
t = t +
}()
return t
} func h() (r int) {
defer func(r int) {
r = r +
}(r)
return
}
-------output-------
0

对于defered函数跟函数命名返回值一块使用的情况, 当无法判断返回值的时候, 需要对函数进行变形.

func f(result int) {
result = 0
func () {
result++
}()
return
}
-------output-------
1

func g() (r int) {
t :=
r = t
func () {
t = t +
}
return
}
-------output-------
5
func h() (r int) {
r =
func (r int) {
r = r +
}(r)
return
}
-------output-------
1

在func(r int) {...}中,由于r是以值传递的方式进行的, 所以r的值不会改变。

注意:
  1. 申请资源后最好立即使用defer关闭资源。

二. panic用法

panic用法挺简单的, 其实就是throw exception。

panic是golang的内建函数,panic会中断函数F的正常执行流程, 从F函数中跳出来, 跳回到F函数的调用者. 对于调用者来说, F看起来就是一个panic, 所以调用者会继续向上跳出, 直到当前goroutine返回. 在跳出的过程中, 进程会保持这个函数栈. 当goroutine退出时, 程序会crash。

要注意的是, F函数中的defered函数会正常执行, 按照上面defer的规则。

同时引起panic除了我们主动调用panic之外, 其他的任何运行时错误, 例如数组越界都会造成panic

看下面一个例子

package main

import (
"fmt"
) func main() {
test()
} func test() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("触发异常")
fmt.Println("test")
}
-------output-------
打印后 
打印中
打印前
panic: 触发异常 goroutine [running]:
main.test()
    D:/Go_Path/go/src/logDemo/main.go: +0x98
    main.main() D:/Go_Path/go/src/logDemo/main.go: +0x27
exit status

三. recover 用法
recover也是golang的一个内建函数, 其实就是try catch。

不过需要注意的是:

  1. recover如果想起作用的话, 必须在defered函数中使用。
  2. 在正常函数执行过程中,调用recover没有任何作用, 他会返回nil。如这样:fmt.Println(recover()) 。
  3. 如果当前的goroutine panic了,那么recover将会捕获这个panic的值,并且让程序正常执行下去。不会让程序crash。

func main() {
fmt.Println("c")
defer func() { // 必须要先声明defer,否则不能捕获到panic异常
fmt.Println("d")
if err := recover(); err != nil {
fmt.Println(err) // 这里的err其实就是panic传入的内容
}
fmt.Println("e")
}()
f() //开始调用f
fmt.Println("f") //这里开始下面代码不会再执行
} func f() {
fmt.Println("a")
panic("异常信息")
fmt.Println("b") //这里开始下面代码不会再执行
}
-------output-------
c
a
d
异常信息
e

参考链接
  1. defer关键字
  2. Golang中defer、return、返回值之间执行顺序的坑

Golang 入门系列(十四)defer, panic和recover用法的更多相关文章

  1. Golang 入门系列(十) mysql数据库的使用

    之前,已经讲过一些Golang的基础的东西,感兴趣的可以看看以前的文章,https://www.cnblogs.com/zhangweizhong/category/1275863.html, 今天简 ...

  2. Golang 入门系列(四)如何理解interface接口

    前面讲了很多Go 语言的基础知识,包括go环境的安装,go语言的语法等,感兴趣的朋友,可以先看看之前的文章.https://www.cnblogs.com/zhangweizhong/category ...

  3. GOLANG错误处理最佳方案errors wrap, Defer, Panic, and Recover

    Simple error handling primitives:        https://github.com/pkg/errors Defer, Panic, and Recover:    ...

  4. 无废话ExtJs 入门教程十四[文本编辑器:Editor]

    无废话ExtJs 入门教程十四[文本编辑器:Editor] extjs技术交流,欢迎加群(201926085) ExtJs自带的编辑器没有图片上传的功能,大部分时候能够满足我们的需要. 但有时候这个功 ...

  5. Bootstrap入门(十四)组件8:媒体对象

    Bootstrap入门(十四)组件8:媒体对象 这是一个抽象的样式,用以构建不同类型的组件,这些组件都具有在文本内容的左或右侧对齐的图片(就像博客评论或 Twitter 消息等). 1.基本样式 2. ...

  6. MyBatis基础入门《十四》ResultMap子元素(association )

    MyBatis基础入门<十四>ResultMap子元素(association ) 1. id: >> 一般对应数据库中改行的主键ID,设置此项可以提高Mybatis的性能 2 ...

  7. struts2官方 中文教程 系列十四:主题Theme

    介绍 当您使用一个Struts 2标签时,例如 <s:select ..../>  在您的web页面中,Struts 2框架会生成HTML,它会显示外观并控制select控件的布局.样式和 ...

  8. RabbitMQ入门教程(十四):RabbitMQ单机集群搭建

    原文:RabbitMQ入门教程(十四):RabbitMQ单机集群搭建 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://b ...

  9. golang 自学系列(四)——debug for vscode

    golang 自学系列(四)--(调试)VSCode For Debug 这里如何装 vscode 我就不说了 这里如何在 vscode 正常写代码我也不说了 在能正常用 vscode 写 go 语言 ...

随机推荐

  1. vscode + platformIO开发stm32f4

    我的电脑环境 win10 vscode 1.36.1 vscode安装插件 安装完这个插件后会提示你安装 platformIOCore,按照提示安装即可.安装过程可能比较缓慢, 可能需要翻墙. 新建项 ...

  2. OAuth 2.0 概念及授权流程梳理

    本文可以转载,但请注明出处https://www.cnblogs.com/hellxz/p/oauth2_process.html OAuth2 的概念 OAuth是一个关于授权的开放网络标准,OAu ...

  3. 封装一个适用于vue的 jsonp

    import originJsonp from 'jsonp' export default function jsonp(url, data, option) { return new Promis ...

  4. EntityFramework Core 3多次Include导致查询性能低之解决方案

    前言 上述我们简单讲解了几个小问题,这节我们再来看看如标题EF Core中多次Include导致出现性能的问题,废话少说,直接开门见山. EntityFramework Core 3多次Include ...

  5. 【JavaEE】之MyBatis插入数据后获取自增主键

    很多时候,我们都需要在插入一条数据后回过头来获取到这条数据在数据表中的自增主键,便于后续操作.针对这个问题,有两种解决方案: 先插入,后查询.我们可以先插入一条数据,然后根据插入的数据的各个字段值,再 ...

  6. Django总结目录

    Django总结目录 1. django框架简介及自定义简易版框架 2. 路由层 3. 视图层 4. 模板层 5. 模型层 5.1 基本操作 5.2 多表操作 5.3 进阶相关 6. 组件 6.1 a ...

  7. Django如何启动源码分析

    Django如何启动源码分析 启动 我们启动Django是通过python manage.py runsever的命令 解决 这句话就是执行manage.py文件,并在命令行发送一个runsever字 ...

  8. Nginx+MySQL+PHP+Redis多机部署(测试发布discuz论坛)

    链接:LNMP+Redis单机部署 1.实战多机部署环境 nginx服务器: 192.168.1.3 php服务器:    192.168.1.4 mysql服务器: 192.168.1.10 red ...

  9. Mac从拆箱到入门

    Mac从拆箱到入门   记录首次使用Mac的我的历程,不是专业的Mac使用教程,只是简单的记录.还有我在使用过程中一些用到的功能都一些小提示吧.  1.首次开机配置,对于一个完全的新手来说(也就是我) ...

  10. ThinkPHP5——模型关联(一对一关联)

    定义 定义一对一关联使用了hasOne,hasOne方法的参数包括: hasOne('关联模型名','外键名','主键名',['模型别名定义'],'join类型'); 下面定义一个用户表,公司给每个用 ...