错误处理
1.1什么是错误
错误是什么?
错误指的是可能出现问题的地方出现了问题。比如打开一个文件时失败,这种情况在人们的意料之中。
而异常指的是不应该出现问题的地方出现了问题。比如引用了空指针,这种情况在人们的意料之外。可见,错误是业务过程的一部分,而异常不是。
Go中的错误也是一种类型。错误用内置的error类型表示。就像其他类型的,如int, float64, 。错误值可以存储在变量中,从函数中返回,等等。
1.2演示错误
让我们从一个示例程序开始,这个程序尝试打开一个不存在的文件。
在os包中有打开文件的功能函数:
func Open(name string) (file *File, err error)
如果文件已经成功打开,那么Open函数将返回文件处理。如果在打开文件时出现错误,将返回一个非nil错误。
func main() {f, err := os.Open(\"test.txt\")if err != nil {fmt.Println(err)return}fmt.Println(f.Name(), \"opened success\")}
如果一个函数或方法返回一个错误,那么按照惯例,它必须是函数返回的最后一个值。因此,Open 函数返回的值是最后一个值。
处理错误的惯用方法是将返回的错误与nil进行此较。nil值表示没有发生错误,而非nil值表示出现错误。在我们的例子中,我们检查错误是否为nil。如果它不是nil,我们只需打印错误并从主函数返回。
1.3错误类型表示
Go语言通过内置的错误接口提供了非常简单的错误处理机制。
让我们再深入一点,看看如何定义错误类型的构建。错误是一个带有以下定义的接口类型,
type error interface {Error() string}
它包含一个带有Error ()字符串的方法。任何实现这个接口的类型都可以作为一个错误使用。这个方法提供了对错误的描述。
当打印错误时,fmt.PrintIn函数在内部调用Error() 方法来获取错误的描述。这就是错误描述是如何在一行中打印出来的。
从错误中提取更多信息的不同方法
既然我们知道错误是一种接口类型,那么让我们看看如何提取更多关于错误的信息。
在上面的例子中,我们仅仅是打印了错误的描述。如果我们想要的是导致错误的文件的实际路径。一种可能的方法是解析错误字符串。
创建error对象
// errors 创建error对象err = errors.New(\"错误信息!!!!\")fmt.Println(err) // 错误信息!!!!fmt.Printf(\"%T\\n\", err) // *errors.errorString// fmt 创建error对象err2 := fmt.Errorf(\"错误信息: %d\", 100)fmt.Println(err2) // 错误信息: 100fmt.Printf(\"%T\\n\", err2) // *errors.errorStringfunc checkAge(age int) error {if age < 0 {//return errors.New(\"年龄不合法\")return fmt.Errorf(\"年龄是:%d,不合法\",age)}return nil}
1.4自定义错误
package mainimport (\"fmt\")/*** @Author: ZSY* @Date: 2020/7/26 21:21* @Desc:*/// 定义一个结构体,表示错误的类型type areaError struct {msg stringlength float64width float64}// 实现error接口,就是实现Error()方法func (e *areaError) Error() string {return e.msg}func (e *areaError) lengthNegative() bool {return e.length < 0}func (e *areaError) widthNegative() bool {return e.width < 0}func rectArea(length, width float64) (float64, error) {msg := \"\"if length < 0 {msg = \"长度小于0\"}if width < 0 {if msg == \"\" {msg = \"宽度小于0\"} else {msg += \",宽度小于0\"}}if msg != \"\" {return 0, &areaError{msg, length, width}}return length * width, nil}func main() {length := -4.0width := -3.0area, err := rectArea(length, width)if err != nil {fmt.Println(err) // 长度小于0,宽度小于0if err, ok := err.(*areaError); ok {if err.lengthNegative() {fmt.Println(\"长度小于0\")}if err.widthNegative() {fmt.Println(\"宽度小于0\")}fmt.Println(err.length) // -4fmt.Println(err.width) // -3fmt.Println(err.msg) // 长度小于0,宽度小于0}return}fmt.Println(\"面积:\", area)}
1.5panic()和recover()
defer
defer 相当于把当前行放到一个栈里面,最后执行,每个方法属于一个区域
函数A()
函数B()
1
2
3
4
5
6
7
8
9
defer 函数B()2。。。
defer 函数B()1。。。。
main…over…
defer main 4。。。。
defer main 3。。。。
func myprint(s string) {fmt.Println(s)}func funA() {fmt.Println(\"函数A()\")}func funB() {fmt.Println(\"函数B()\")defer myprint(\"defer 函数B()1。。。。\")for i := 1; i < 10; i++ {fmt.Println(i)}defer myprint(\"defer 函数B()2。。。\")}func main() {funA()defer myprint(\"defer main 3。。。。\")funB()defer myprint(\"defer main 4。。。。\")fmt.Println(\"main..over....\")}
panic
panic 其后的所有都不能执行了中断了,只有已经被执行defer的函数执行完后,这个恐慌(panic)才会传到函数调用处
函数A()
函数B()
1
2
3
4
5
defer 函数B()1。。。。
defer main 3。。。。
panic: 函数B() panic
goroutine 1 [running]:
main.funB()
D:/go/GoWorks/src/basic/panicdemo.go:24 +0x1f2
main.main()
D:/go/GoWorks/src/basic/panicdemo.go:33 +0xda
package mainimport \"fmt\"/*** @Author: ZSY* @Date: 2020/7/26 21:39* @Desc:*/func myprint(s string) {fmt.Println(s)}func funA() {fmt.Println(\"函数A()\")}func funB() {fmt.Println(\"函数B()\")defer myprint(\"defer 函数B()1。。。。\")for i := 1; i < 10; i++ {fmt.Println(i)if i == 5 {panic(\"函数B() panic\")}}defer myprint(\"defer 函数B()2。。。\")}func main() {funA()defer myprint(\"defer main 3。。。。\")funB()defer myprint(\"defer main 4。。。。\")fmt.Println(\"main..over....\")}
recover
可以捕获panic,还可以获取panic中的信息
panic — try
recover – catch
函数A()
函数B()
1
2
3
4
5
defer 函数B()1。。。。
函数B() panic 程序恢复
main…over…
defer main 4。。。。
defer main 3。。。。
package mainimport \"fmt\"/*** @Author: ZSY* @Date: 2020/7/26 21:39* @Desc:*/func myprint(s string) {fmt.Println(s)}func funA() {fmt.Println(\"函数A()\")}func funB() {defer func() {if msg := recover(); msg != nil {fmt.Println(msg, \"程序恢复\")}}()fmt.Println(\"函数B()\")defer myprint(\"defer 函数B()1。。。。\")for i := 1; i < 10; i++ {fmt.Println(i)if i == 5 {panic(\"函数B() panic\")}}defer myprint(\"defer 函数B()2。。。\")}func main() {funA()defer myprint(\"defer main 3。。。。\")funB()defer myprint(\"defer main 4。。。。\")fmt.Println(\"main..over....\")}