首页>>后端>>Golang->Go语言 错误处理以及资源管理

Go语言 错误处理以及资源管理

时间:2023-11-30 本站 点击:0

defer 调用

通常用于资源管理,defer可以确保在函数结束时调用

functryDefer(){deferfmt.Println(1)deferfmt.Println(2)fmt.Println(3)}funcmain(){tryDefer()}

看下 打印顺序 可以看出来: defer是可以确保 程序一定会执行的,哪怕中间return 或者panic 都不怕 defer本身的执行顺序 是 先进后出

来看下defer的具体应用

funcwriteFile(filenamestring){//创建一个文件file,err:=os.Create(filename)iferr!=nil{panic(err)}//既然open了一个文件那一定要记得关闭deferfile.Close()//往缓存里面写入数据writer:=bufio.NewWriter(file)//既然使用了缓存那么一定要记住要flushdeferwriter.Flush()//往缓存中写入对应的数据fmt.Fprintln(writer,"hellogolangwuyue111")}

还是挺好用的,和java相比 这个defer的特性可以确保我们不会出现忘记关闭资源的bug

另外要注意的是参数是在defer语句是进行计算

举例:

functryDefer(){fori:=0;i<10;i++{deferfmt.Println(i)ifi==5{panic("toomanytimes")}}}

看下执行结果:

错误处理

之前的程序 我们可以看出来,panic是直接让程序终止的,就好像类似于java里面 丢了一个没人能处理的异常 直接导致程序运行停止了。正常情况下 我们应该避免这种情况。所以 才需要正确的错误处理方式

比如:

funcwriteFile2(filenamestring){//创建一个文件如果之前已经存在这个文件那么直接报错file,err:=os.OpenFile(filename,os.O_EXCL|os.O_CREATE,0666)iferr!=nil{fmt.Println("filealeadyexists")}deferfile.Close()}

这样的提示方式是比较友好的,我们当然可以看看这个error到底是个啥

嗯 就是一个接口,只不过这个接口是一个Error方法

funcwriteFile2(filenamestring){//创建一个文件如果之前已经存在这个文件那么直接报错file,err:=os.OpenFile(filename,os.O_EXCL|os.O_CREATE,0666)iferr!=nil{fmt.Println(err.Error())}deferfile.Close()}

那到这里呢,我们还可以继续看看这个openfile函数

意思就是 如果这里发生了错误 那一定是一个patherror的指针类型

funcwriteFile2(filenamestring){//创建一个文件如果之前已经存在这个文件那么直接报错file,err:=os.OpenFile(filename,os.O_EXCL|os.O_CREATE,0666)iferr!=nil{pathError,ok:=err.(*os.PathError)if!ok{panic(err)}else{fmt.Println("op:",pathError.Op,"path:",pathError.Path,"err:",pathError.Err)}}deferfile.Close()}

那我们继续来看一下:

另外就是 一般情况下,我们错误处理的时候 还会加个return语句 确保不会在后面的程序中 走入错误的逻辑

funcwriteFile2(filenamestring){//创建一个文件如果之前已经存在这个文件那么直接报错file,err:=os.OpenFile(filename,os.O_EXCL|os.O_CREATE,0666)iferr!=nil{pathError,ok:=err.(*os.PathError)if!ok{panic(err)}else{fmt.Println("op:",pathError.Op,"path:",pathError.Path,"err:",pathError.Err)}//错误处理的时候别忘记returnreturn}deferfile.Close()}

当然我们也可以自己顺手创建一个error

funcwriteFile2(filenamestring){//创建一个文件如果之前已经存在这个文件那么直接报错file,err:=os.OpenFile(filename,os.O_EXCL|os.O_CREATE,0666)//当然也可以自己创建一个erroriffilename=="test.txt"{err=errors.New("filenamecannotbetest.txt")}iferr!=nil{pathError,ok:=err.(*os.PathError)if!ok{panic(err)}else{fmt.Println("op:",pathError.Op,"path:",pathError.Path,"err:",pathError.Err)}//错误处理的时候别忘记returnreturn}deferfile.Close()}

统一处理错误逻辑

来写个简单的web程序吧

你看我这个下面 有这么多go文件 我希望在浏览器 输入对应的路径的时候 可以看到对应go文件的内容

程序很简单:

funcmain(){http.HandleFunc("/list/",func(writerhttp.ResponseWriter,request*http.Request){//看下传来的pathpath:=request.URL.Path[len("/list/"):]//找到对应的文件file,err:=os.Open(path)iferr!=nil{panic(err)}deferfile.Close()//读取文件的内容all,err:=ioutil.ReadAll(file)iferr!=nil{panic(err)}//将文件的内容输出writer.Write(all)})err:=http.ListenAndServe(":8888",nil)iferr!=nil{panic(err)}}

也可以看到效果:

那如果输入一个错误的路径会得到啥呢?

显然这是不正确的,看下我们的server端:

虽然http库对server的panic做了保护 不会让服务器直接挂掉,但是这里访问的人在浏览器上的体验就极其糟糕了

改进一下:

funcmain(){http.HandleFunc("/list/",func(writerhttp.ResponseWriter,request*http.Request){//看下传来的pathpath:=request.URL.Path[len("/list/"):]//找到对应的文件file,err:=os.Open(path)iferr!=nil{//改进一下提供一些正常的错误信息http.Error(writer,err.Error(),http.StatusInternalServerError)return}deferfile.Close()//读取文件的内容all,err:=ioutil.ReadAll(file)iferr!=nil{panic(err)}//将文件的内容输出writer.Write(all)})err:=http.ListenAndServe(":8888",nil)iferr!=nil{panic(err)}}

这样看起来显然正常的多

除此之外 这里的错误处理 显然要改的地方太多,正常情况下 我们应该写一个函数 统一处理

首先我们可以将handle函数 的所有error 都返回出来

funcwriteFile(filenamestring){//创建一个文件file,err:=os.Create(filename)iferr!=nil{panic(err)}//既然open了一个文件那一定要记得关闭deferfile.Close()//往缓存里面写入数据writer:=bufio.NewWriter(file)//既然使用了缓存那么一定要记住要flushdeferwriter.Flush()//往缓存中写入对应的数据fmt.Fprintln(writer,"hellogolangwuyue111")}0

为了方便呢,我们给这个函数取一个别名:

funcwriteFile(filenamestring){//创建一个文件file,err:=os.Create(filename)iferr!=nil{panic(err)}//既然open了一个文件那一定要记得关闭deferfile.Close()//往缓存里面写入数据writer:=bufio.NewWriter(file)//既然使用了缓存那么一定要记住要flushdeferwriter.Flush()//往缓存中写入对应的数据fmt.Fprintln(writer,"hellogolangwuyue111")}1

最终呢我们处理一下这个函数的error

funcwriteFile(filenamestring){//创建一个文件file,err:=os.Create(filename)iferr!=nil{panic(err)}//既然open了一个文件那一定要记得关闭deferfile.Close()//往缓存里面写入数据writer:=bufio.NewWriter(file)//既然使用了缓存那么一定要记住要flushdeferwriter.Flush()//往缓存中写入对应的数据fmt.Fprintln(writer,"hellogolangwuyue111")}2

最终呢 就换一种形式去调用她:

funcwriteFile(filenamestring){//创建一个文件file,err:=os.Create(filename)iferr!=nil{panic(err)}//既然open了一个文件那一定要记得关闭deferfile.Close()//往缓存里面写入数据writer:=bufio.NewWriter(file)//既然使用了缓存那么一定要记住要flushdeferwriter.Flush()//往缓存中写入对应的数据fmt.Fprintln(writer,"hellogolangwuyue111")}3

panic

前面我们见到了数次使用panic的地方,总结一下panic关键字的作用:

停止当前函数的执行 panic会一直向上返回 执行每层的defer 如果没有遇见recover 程序就退出了。

与之对应的还有recover这个关键函数

recover只能在defer调用中使用 并且还可以获取panic的值,无法处理panic的时候可以重新pani

funcwriteFile(filenamestring){//创建一个文件file,err:=os.Create(filename)iferr!=nil{panic(err)}//既然open了一个文件那一定要记得关闭deferfile.Close()//往缓存里面写入数据writer:=bufio.NewWriter(file)//既然使用了缓存那么一定要记住要flushdeferwriter.Flush()//往缓存中写入对应的数据fmt.Fprintln(writer,"hellogolangwuyue111")}4

可以看下具体两种不同的panic的时候 程序的运行状态

除此之外呢 这里panic是有可能为nil的,所以我们必须进行判空

funcwriteFile(filenamestring){//创建一个文件file,err:=os.Create(filename)iferr!=nil{panic(err)}//既然open了一个文件那一定要记得关闭deferfile.Close()//往缓存里面写入数据writer:=bufio.NewWriter(file)//既然使用了缓存那么一定要记住要flushdeferwriter.Flush()//往缓存中写入对应的数据fmt.Fprintln(writer,"hellogolangwuyue111")}5

最后总结一下,自己在设计程序的时候:

意料之中的错误 用error,意料之外的用panic


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