Golang 笔记 4 defer、error、panic
一、defer语句
defer语句仅能被放置在函数或方法中。它由关键字defer和一个调用表达式组成。这里的表达式所表示的既不能是对Go语言内建函数的调用也不能是对Go语言标准库代码包unsafe中的那些函数的调用。实际上,满足上述条件的表达式被称为表达式语句。例:
func readFile(path string) ([]byte, error) {
file,err != os.Open(path)
if err != nil {
return nil,err
}
defer file.Close()
return ioutil.ReadAll(file)
}
函数readFile的功能是读取指定文件或目录本身的内容并将其返回,同时当有错误发生时立即向调用方报告。其中os和ioutil代表的都是Go语言标准库中的代码包。在打开文件且未发现有错误发生之后,紧跟了一条defer语句。其中携带的表达式语句表示的是对被打开文件的关闭操作。当这条defer语句被执行的时候,其中的这条表达式语句并不会被执行。它的确切的执行时机是在其所属的函数(这里是readFile)的执行即将结束的那个时刻。也就是说,在readFile函数真正结束执行的前一刻,file.Close()才会被执行。该语句可保证在readFile函数将结果返回给调用方之前,那个文件或目录一定会被关闭。
无论readFile函数正常返回还是发生了异常其中的file.Close()都会在该函数即将退出那一刻被执行。
当一个函数中存在多个defer语句时,会按出现顺序的倒序执行。例:
func deferIt() {
defer func(){
fmt.Print(1)
}()
defer func() {
fmt.Print(2)
}()
defer func() {
fmt.Print(3)
}()
fmt.Print(4)
}
deferIt()的输出结果是4321。
defer携带的表达式语句代表的是对某个函数或方法的调用。这个调用可能会有参数传入,比如:fmt.Print(i+1)。如果代表传入参数是一个表达式,那么在defer语句被执行的时候该表达式就会被求值了。这与被携带的表达式语句的执行时机是不同的。
func deferIt3() {
f := func(i int) int {
fmt.Printf("%d", i)
return i * 10
}
for i := 1; i < 5; i++ {
defer fmt.Printf("%d", f(i))
}
}
输出结果为1 2 3 4 40 30 20 10
如果defer携带的表达式代表的是对匿名函数的调用,那么我们就一定要非常警惕:
funct deferIt4() {
for i := 1; i < 5; i++ {
defer func() {
fmt.Print(i)
}()
}
}
此函数执行后会输出5555,而不是4321。原因是defer语句携带的表达式语句中的那个匿名函数包含了对外部的变量的使用。等待这个匿名函数要被执行的时候,包含该defer语句的那条for语句已经执行完毕了。此时的变量i的值已经变为5,因此该匿名函数只会打印5。正确的用法是:把要使用的外部变量作为参数传入到匿名函数中
func deferIt4() {
for i := 1; i < 5; i++ {
defer func(n int) {
fmt.Print(n)
}()
}
}
二、Go语言错误处理 error
Go语言的函数可以一次返回多个结果。上一节中例子:
func readFile(path string) ([]byte, errro) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
return ioutil.ReadAll(file)
}
函数readFile有两个结果声明。第二个结果声明的类型是error。error是Go语言内置的一个接口类型。它的声明如下:
type error interface {
Error() string
}
显然只要一个类型的方法集合包含了名为Error、无参数声明且仅声明了一个string类型的结果的方法,就相当实现了error接口。os.Open函数的第二个结果值的类型就是这样的。我们把它赋给了变量err。
在调用了os.Open函数并取得其结果之后,我们判断err是否为nil。如果答案不是则直接返回该错误。
&nnbsp; readFile函数的最后一条语句是return,它把ioutil.ReadAll函数的调用结果直接作为readFile函数的结果返回。实际上,ioutil.ReadAll函数的结果声明列表与readFile的结果声明列表是一致的。
接下来说明一下如何创建错误:只需调用标准库代码包errors的New函数即可。例,可以在readFile函数的开始处加入这段代码可以在参数无效的时候告知调用方:
if path == "" {
return nil, errors.New("The parameter is invalid!")
}
Go语言标准库的代码包中有很多由errors.New函数创建出来的错误值。例:os.ErrPermission、io.EOF。我们可以方便的用操作符==来判断一个error类型的值与这些变量的值是否相等,从而来确定错误的具体类型。比如io.EOF,它代表读取方已无更多数据可读,我们在得到这个错误的时候不该把它看成一个错误而应该只去结束相应的读取操作。
if err == io.EOF {
...
}
三、Go语言异常处理 panic
可以把panic理解为异常。如果不显式的处理panic程序会崩溃。内建函数panic可以人为地产生一个异常。不过,这种致命错误可以被恢复。在Go中,内建函数recover可以做到这一点。
recocer函数必须要在defer语句中调用才有效。因为一旦有异常产生,当前函数以及在调用栈上的所有代码都会失去对流程的控制权。只有defer语句携带的函数中的代码才可以在异常时拦截到。例:
defer func() {
if p := recover(); p != nil {
fmt.Printf("Fatal error: %s\n", p)
}
}
recover函数会返回一个interface{}类型的值,interface{}代表空接口。Go中的任何类型都是它的实现类型。如果p不为nil那么就说明当前确有异常发生。这时我们要根据情况做相应处理。一旦defer语句中的recover函数调用被执行了,异常就会被恢复,不论我们是否进行了后续处理。我们一定不要只拦截不处理。
panic函数可接受一个interface{}类型的值作为其参数,即我们可以传任何类型的参数给panic。这里最好只传error类型的值。
Golang 笔记 4 defer、error、panic的更多相关文章
- Golang 高效实践之defer、panic、recover实践
前言 我们知道Golang处理异常是用error返回的方式,然后调用方根据error的值走不同的处理逻辑.但是,如果程序触发其他的严重异常,比如说数组越界,程序就要直接崩溃.Golang有没有一种异常 ...
- Golang错误处理函数defer、panic、recover、errors.New介绍
在默认情况下,当发生错误(panic)后,程序就会终止运行 如果发生错误后,可以捕获错误,并通知管理人员(邮件或者短信),程序还可以继续运行,这当然无可厚非 errors.New("错误信息 ...
- Golang笔记(一)简洁的语言风格
Golang笔记(一)简洁的语言风格 概述 Golang继承了很多C语言的风格,寡人使用了十几年C语言,切换到Golang时上手很快,并且随着深入的使用,越来越喜欢这门语言.Golang最直观的感受是 ...
- golang 详解defer
什么是defer defer用来声明一个延迟函数,把这个函数放入到一个调用链表上, 当外部的包含方法return之前,返回参数到调用方法之前调用,也可以说是运行到最外层方法体的"}" ...
- go语言中使用defer、panic、recover处理异常
go语言中的异常处理,没有try...catch等,而是使用defer.panic.recover来处理异常. 1.首先,panic 是用来表示非常严重的不可恢复的错误的.在Go语言中这是一个内置函数 ...
- 理解Defer、Panic和Recover
刚开始的时候理解如何使用Defer和Recover有一点怪异,尤其是使用了try/catch块的时候.有一种模式可以在Go中实现和try/catch语句块一样的效果.不过之前你需要先领会Defer.P ...
- Golang笔记(二)面向对象的设计
Golang笔记(二)面向对象的设计 Golang本质还是面向过程的语言,但它实现了一些OOP的特性,包括抽象.封装.继承和多态. 抽象和封装 Golang和C语言一样以struct为数据结构核心,不 ...
- golang学习之defer
golang中的defer通常用于执行一些资源释放性操作,比如open/close.connect/disconnect.lock/unlock等,对defer理解主要记住以下三点: 1.defer ...
- golang笔记1
golang笔记1 go代码是用包来组织的,每个包有一个或多个go文件组成,这些go文件文件放在一个文件夹中 每个源文件开始都用一个package声明,指明本源文件属于哪个包 pakage声明后紧跟这 ...
随机推荐
- jsp下载文件的实现方法及注意事项 (转)
jsp中实现文件下载,最简单的方式是在网页上做超级链接,如:<a href="music/abc.mp3">点击下载</a>. 但是,这样服务器上的目录资源 ...
- JDBC(8)—Blob
Blob LOB,即:Large Objects(大对象),是用来存储大量的二进制和文本数据的一种数据类型(一个lob字段可以存储多达四个G的数据).LOB分为两种类型:内部LOB和外部LOB --内 ...
- [Functional Programming] Functional JS - Pointfree Logic Functions
Learning notes. Video. Less than: If you use 'ramda', you maybe know 'lt, gt'.. R.lt(2, 1); //=> ...
- CTR预估算法之FM, FFM, DeepFM及实践
https://blog.csdn.net/john_xyz/article/details/78933253 目录目录CTR预估综述Factorization Machines(FM)算法原理代码实 ...
- SSE图像算法优化系列九:灵活运用SIMD指令16倍提升Sobel边缘检测的速度(4000*3000的24位图像时间由480ms降低到30ms)。
这半年多时间,基本都在折腾一些基本的优化,有很多都是十几年前的技术了,从随大流的角度来考虑,研究这些东西在很多人看来是浪费时间了,即不能赚钱,也对工作能力提升无啥帮助.可我觉得人类所谓的幸福,可以分为 ...
- SpringBoot2.0小程序支付功能实现weixin-java-pay
SpringBoot2.0小程序支付功能实现weixin-java-pay WxJava - 微信开发 Java SDK(开发工具包); 支持包括微信支付.开放平台.公众号.企业微信/企业号.小程序等 ...
- 移动应用开发技术选型:WebApp>HybridApp>NativeApp
一:概念辨析 Web App:生存在浏览器里的应用,只能运行在浏览器里,宿主是浏览器,不是操作系统.资源一般都在网络上,就是一个触屏版的网站.如:微信公众号.不需要在设备上下载安装,只需通过浏览器即可 ...
- 修改openssh显示版本号
问题描述: 漏洞安全对使用的软件扫描漏洞,都是依据软件的版本号探测的,直接升级软件风险太大,因此规避风险,修改软件版本号实现其目的! 问题解决: strings xxx |grep xxx //实 ...
- 批量替换存储过程内容脚本sp_SqlReplace
开始 在数据库开发过程中,如果某一个表字段名被重命名.那么在使用到该字段的存储过程,对应的表字段名也要修改. 当存在多个存储都有使用该表字段,需要逐个去修改替换,是一件比较繁琐的事情,我们需要一个能实 ...
- SNF软件开发机器人-子系统-功能-启用大按钮样式如何配置
启用大按钮 当启用大按钮被选中后,页面的按钮图表将以按钮配置中的大按钮样式显示. 1.效果展示: 2.使用说明: 打开显示页面,点击开发者选项的简单配置按钮.在功能表信息中选择启用大按钮复选框后保存.