在程序执行过程中出现的不正常情况称为错误
Go语言中使用builtin包下error接口作为错误类型,官方源码定义如下
只包含了一个方法,方法返回值是string,表示错误信息
// The error built-in interface type is the conventional interface for // representing an error condition, with the nil value representing no error. type error interface { Error() string }Go语言中错误都作为方法/函数的返回值,因为Go语言认为使用其他语言类似try...catch这种方式会影响到程序结构
在Go语言标准库的errors包中提供了error接口的实现结构体errorString,并重写了error接口的Error()方法.额外还提供了快速创建错误的函数
package errors // New returns an error that formats as the given text. func New(text string) error { return &errorString{text} } // errorString is a trivial implementation of error. type errorString struct { s string } func (e *errorString) Error() string { return e.s } 如果错误信息由很多变量(小块)组成,可以借助fmt.Errorf("verb",...)完成错误信息格式化,因为底层还是errors.New()
// Errorf formats according to a format specifier and returns the string // as a value that satisfies error. func Errorf(format string, a ...interface{}) error { return errors.New(Sprintf(format, a...)) }使用Go语言标准库创建错误,并返回
func demo(i, k int) (d int, e error) { if k == 0 { e = errors.New("初始不能为0") d=0 return } d = i / k return } func main() { result,error:=demo(6,0) fmt.Println(result,error) }如果错误信息由多个内容组成,可以使用下面实现方式
func demo(i, k int) (d int, e error) { if k == 0 { e = fmt.Errorf("%s%d和%d", "除数不能是0,两个参数分别是:", i, k) d = 0 return } d = i / k return } func main() { result, error := demo(6, 0) fmt.Println(result, error) }可以忽略错误信息,使用占位符
func demo(i, k int) (d int, e error) { if k == 0 { e = fmt.Errorf("%s%d和%d", "除数不能是0,两个参数分别是:", i, k) d = 0 return } d = i / k return } func main() { result, _ := demo(6, 0) fmt.Println(result) }使用if处理错误,原则上每个错误都应该解决
func demo(i, k int) (d int, e error) { if k == 0 { e = fmt.Errorf("%s%d和%d", "除数不能是0,两个参数分别是:", i, k) d = 0 return } d = i / k return } func main() { result, error := demo(6, 0) if error != nil { fmt.Println("发生错误", error) return } fmt.Println("程序执行成功,结果为:", result) }Go语言中defer可以完成延迟功能,当前函数执行完成后执行defer功能
defer最常用的就是关闭连接(数据库连接,文件等)可以打开连接后代码紧跟defer进行关闭,后面在执行其他功能
在很多语言中要求必须按照顺序执行,也就是必须把关闭代码写在最后,但是经常会忘记关闭导致内存溢出,而Golang中defer很好的解决了这个问题.无论defer写到哪里都是最后执行
func main() { fmt.Println("打开连接") defer func(){ fmt.Println("关闭连接") }() fmt.Println("进行操作") //输出:打开连接 进行操作 关闭连接 }多重defer采用栈结构执行,先产生后执行
在很多代码结构中都可能出现产生多个对象,而程序希望这些对象倒序关闭,多个defer正好可以解决这个问题
func main() { fmt.Println("打开连接A") defer func(){ fmt.Println("关闭连接A") }() fmt.Println("打开连接B") defer func(){ fmt.Println("关闭连接B") }() fmt.Println("进行操作") //输出:打开连接A 打开连接B 进行操作 关闭连接B 关闭连接A }defer与return同时存在时,要把return理解成两条执行结合(不是原子指令),一个指令是给返回值赋值,另一个指令返回跳出函数
defer和return时整体执行顺序
先给返回值赋值
执行defer
返回跳出函数
没有定义返回值接收变量,执行defer时返回值已经赋值
func f() int{ i:=0 defer func(){ i=i+2 }() return i } func main() { fmt.Println(f())//输出:0 }声明接收返回值变量,执行defer时修改了返回值内容.
由于return后面没有内容,就无法给返回值赋值,所以执行defer时返回值才有内容
func f() (i int){ defer func(){ i=i+2 }() return } func main() { fmt.Println(f())//输出:2 }panic是builtin中函数
// The panic built-in function stops normal execution of the current // goroutine. When a function F calls panic, normal execution of F stops // immediately. Any functions whose execution was deferred by F are run in // the usual way, and then F returns to its caller. To the caller G, the // invocation of F then behaves like a call to panic, terminating G's // execution and running any deferred functions. This continues until all // functions in the executing goroutine have stopped, in reverse order. At // that point, the program is terminated and the error condition is reported, // including the value of the argument to panic. This termination sequence // is called panicking and can be controlled by the built-in function // recover. func panic(v interface{})panic有点类似与其他编程语言的throw,抛出异常.当执行到panic后终止剩余代码执行.并打印错误栈信息
func main() { fmt.Println("1") panic("panic执行了,哈哈") fmt.Println("2") }执行结果
1 panic: panic执行了,哈哈 goroutine 1 [running]: main.main() D:/gowork/c/main.go:7 +0x80注意panic不是立即停止程序(os.Exit(0)),defer还是执行的.
func main() { defer func(){ fmt.Println("defer执行") }() fmt.Println("1") panic("panic执行了,哈哈") fmt.Println("2") } 1 defer执行 panic: panic执行了,哈哈 goroutine 1 [running]: main.main() D:/project/src/first/main.go:114 +0xc5 Process finished with exit code 2recover()表示恢复程序的panic(),让程序正常运行
recover()是和panic(v)一样都是builtin中函数,可以接收panic的信息,恢复程序的正常运行
// The recover built-in function allows a program to manage behavior of a // panicking goroutine. Executing a call to recover inside a deferred // function (but not any function called by it) stops the panicking sequence // by restoring normal execution and retrieves the error value passed to the // call of panic. If recover is called outside the deferred function it will // not stop a panicking sequence. In this case, or when the goroutine is not // panicking, or if the argument supplied to panic was nil, recover returns // nil. Thus the return value from recover reports whether the goroutine is // panicking. func recover() interface{}recover()一般用在defer内部,如果没有panic信息返回nil,如果有panic,recover会把panic状态取消
func main() { defer func() { if error:=recover();error!=nil{ fmt.Println("出现了panic,使用reover获取信息:",error) } }() fmt.Println("11111111111") panic("出现panic") fmt.Println("22222222222") }输出
11111111111 出现了panic,使用reover获取信息: 出现panicrecover()只能恢复当前函数级或当前函数调用函数中的panic(),恢复后调用当前级别函数结束,但是调用此函数的函数可以继续执行.
panic会一直向上传递,如果没有recover()则表示终止程序,但是碰见了recover(),recover()所在级别函数表示没有panic,panic就不会向上传递
func demo1(){ fmt.Println("demo1上半部分") demo2() fmt.Println("demo1下半部分") } func demo2(){ defer func() { recover()//此处进行恢复 }() fmt.Println("demo2上半部分") demo3() fmt.Println("demo2下半部分") } func demo3(){ fmt.Println("demo3上半部分") panic("在demo3出现了panic") fmt.Println("demo3下半部分") } func main() { fmt.Println("程序开始") demo1() fmt.Println("程序结束") }输出结果:
程序开始 demo1上半部分 demo2上半部分 demo3上半部分 demo1下半部分 程序结束