知识点:
1.异常处理 defer,panic,recover
2.error
3.自定义错误类型
异常处理:
在异常处理方面,Go语言不像其他语言,使用try..catch.. finall..., 而使用defer, panic, recover,将异常和控制流程区分开。即通过panic抛出异常,然后在defer中,通过recover捕获这个异常,最后处理。
但是更加推荐的错误处理方法:
Golang中我们通常会在函数或方法中返回error
结构对象来判断是否有异常出现,并且可以更具需要自定义各种类型的error。如果返回的 error 值为 nil,则表示未遇到错误,否则 error 会返回一个字符串,用于说明遇到了什么错误。
type error interface {
Error() string
}
golang 中内置的错误类型 error 是一个接口类型,自定义的错误类型必须实现 Error()方法
如何生成error?
方式一:New方法 原生
将字符串 text 包装成一个 error 对象返回
func New(text string) error {
return &errorString{text}
}
//例如
var ErrShortWrite = errors.New("short write")
方式二:定义自己的错误
package main
import (
"fmt"
"time"
)
// MyError is an error implementation that includes a time and message.
type MyError struct {
When time.Time
What string
}
func (e MyError) Error() string {
return fmt.Sprintf("%v: %v", e.When, e.What)
}
func oops() error {
return MyError{
time.Date(1989, 3, 15, 22, 30, 0, 0, time.UTC),
"the file system has gone away",
}
}
func main() {
if err := oops(); err != nil {
fmt.Println(err)
}
}
defer:
defer是Go语言中的延迟执行语句
,用来添加函数结束时执行的代码,常用于释放某些已分配的资源、关闭数据库连接、断开socket连接、解锁一个加锁的资源。
(1)defer执行顺序:
return xxx:赋值指令 + CALL defer指令 + RET指令
return xxx会被改写成:
返回值 = xxx
调用defer函数
空的return
整个return过程,没有defer之前,先在栈中写一个值,这个值会被当作返回值,然后再调用RET指令返回。return xxx语句汇编后是 1.先给返回值赋值,2.再做一个空的return,( 赋值指令 + RET指令)。defer的执行是被插入到return指令之前的,有了defer之后,就变成了(赋值指令 + CALL defer指令 + RET指令)。
而在CALL defer函数中,有可能将最终的返回值改写了...也有可能没改写。总之,return xxx不是一条原子指令,如果返回值改写了,那么看上去就像defer是在return xxx之后执行的~
example1
func f() (result int) {
defer func() {
result++
}()
return 0
}
返回值:1
example2
func f() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
返回值:5
example3
func f() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
返回值:1
example4
func f() (r int) {
return 1
defer func() {
r++
r++
}()
return 2
}
返回值:1
注意:如果去掉了return 2,会出现语法错误:Missing return at the end of function
example5
func main() {
for i:=0 ;i<5; i++ {
defer func() {
fmt.Println(i)
}()
}
fmt.Println("test:")
}
输出:
test:
5
5
5
5
5
func main() {
for i:=0 ;i<5; i++ {
defer func(i int) {
fmt.Println(i)
}(i)
}
fmt.Println("test:")
}
输出:
test:
4
3
2
1
0
(2)defer栈
如果一个函数中有多个defer语句,它们会以LIFO(后进先出)的顺序执行。哪怕函数或某个延迟调用发生错误,这些调用依旧会被执。
package main //必须
import "fmt"
func test(x int) {
result := 100 / x
fmt.Println("result = ", result)
defer fmt.Println("three")
}
func main() {
defer fmt.Println("one")
defer fmt.Println("two")
//调用一个函数,导致内存出问题,除数不能为0
defer test(0)
defer fmt.Println("four")
}
注:defer 需要放在 panic 之前定义,因为由panic引发异常以后,程序停止执行,即不再执行下面代码,放在后面调用不了defer
输出结果:
panic:
当程序遇到致命错误导致无法继续运行时就会触发panic,例如:数据越界,空指针等。我们可以通过主动调用 panic() 函数,抛出致命的错误。
通过使用recover()函数,我可以捕获上述的错误异常
golang 的错误处理流程:当一个函数在执行过程中出现了异常或遇到
panic(),正常语句就会立即终止,然后执行 defer 语句,再报告异
常信息,最后退出 goroutine。如果在 defer 中使用了 recover()
函数,则会捕获错误信息,使该错误信息终止报告。
panic会在调用它的函数中向本层和它的所有上层逐级抛出,若一直没有recover将其捕获,程序退出后会产生crash。
例如:
package main //必须
import "fmt"
func test(x int) {
defer func() {
if err:=recover(); err!=nil{
fmt.Println(err)
}
}()
result := 100 / x
fmt.Println("result = ", result)
}
func main() {
//调用一个函数,导致内存出问题,除数不能为0
test(0)
defer fmt.Println("end")
}
输出:
runtime error: integer divide by zero
end
注意:
1.defer要定义在panic之前
2.recover()的调用仅当它在defer函数
中被直接调用
时才有效。在正常的执行过程中,调用 recover 会返回 nil 并且没有其他任何效果.
参考:http://www.coder55.com/articl...
有疑问加站长微信联系(非本文作者)