在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
①error接口 Go语言中的error类型实际上是抽象了Error()方法的error接口 type error interface { Error() string } Go语言使用该接口进行标准的错误处理。 对于大多数函数,如果要返回错误,大致上都可以定义为如下模式,将error作为多种返回 func Foo(param int)(n int, err error) { // ... } 调用时的代码建议按如下方式处理错误情况: n, err := Foo(0) if err != nil { // 错误处理 } else { // 使用返回值n } 看下面的例子综合了一下error接口的用法: package main import ( "fmt" ) //自定义错误类型 type ArithmeticError struct { error //实现error接口 } //重写Error()方法 func (this *ArithmeticError) Error() string { return "自定义的error,error名称为算数不合法" } //定义除法运算函数 func Devide(num1, num2 int) (rs int, err error) { if num2 == 0 { return 0, &ArithmeticError{} } else { return num1 / num2, nil } } func main() { var a, b int fmt.Scanf("%d %d", &a, &b) rs, err := Devide(a, b) if err != nil { fmt.Println(err) } else { fmt.Println("结果是:", rs) } }
运行,输入参数5 2(正确的情况): 5 2 结果是: 2
若输入5 0(产生错误的情况): 5 0 自定义的error,error名称为算数不合法
通过上面的例子可以看出error类型类似于Java中的Exception类型,不同的是Exception必须搭配throw和catch使用。 ②defer--延迟语句 在Go语言中,可以使用关键字defer向函数注册退出调用,即主调函数退出时,defer后的函数才会被调用。 当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。 例如: package main import ( "fmt" ) func main() { for i := 0; i < 5; i++ { defer fmt.Println(i) } }
其执行结果为: 4 3 2 1 0
defer语句在声明时被加载到内存(多个defer语句按照FIFO原则) ,加载时记录变量的值,而在函数返回之后执行,看下面的例子: 例子1:defer语句加载时记录值 func f1() { i := 0 defer fmt.Println(i) //实际上是将fmt.Println(0)加载到内存 i++ return } func main() { f1() }
其结果显然是0 例子2:在函数返回后执行 func f2() (i int) { var a int = 1 defer func() { a++ fmt.Println("defer内部", a) }() return a } func main() { fmt.Println("main中", f2()) }
其结果是
defer内部 2 main中 1
例子3:defer语句会读取主调函数的返回值,并对返回值赋值.(注意和例子2的区别) func f3() (i int) { defer func() { i++ }() return 1 } func main() { fmt.Println(f3()) }
其结果竟然是2. 通过上面的几个例子,自然而然会想到用defer语句做清理工作,释放内存资源(这样你再也不会为Java中的try-catch-finally层层嵌套而苦恼了) 例如关闭文件句柄: srcFile,err := os.Open("myFile") defer srcFile.Close()
关闭互斥锁: mutex.Lock()
defer mutex.Unlock()
上面例子中defer语句的用法有两个优点: 1.让设计者永远也不会忘记关闭文件,有时当函数返回时常常忘记释放打开的资源变量。 2.将关闭和打开靠在一起,程序的意图变得清晰很多。 下面看一个文件复制的例子: package main import ( "fmt" "io" "os" ) func main() { copylen, err := copyFile("dst.txt", "src.txt") if err != nil { return } else { fmt.Println(copylen) } } //函数copyFile的功能是将源文件sec的数据复制给dst func copyFile(dstName, srcName string) (copylen int64, err error) { src, err := os.Open(srcName) if err != nil { return } //当return时就会调用src.Close()把源文件关闭 defer src.Close() dst, err := os.Create(dstName) if err != nil { return } //当return是就会调用src.Close()把目标文件关闭 defer dst.Close() return io.Copy(dst, src) }
可以看到确实比Java简洁许多。 ③panic-recover运行时异常处理机制 Go语言中没有Java中那种try-catch-finally结构化异常处理机制,而使用panic()函数答题throw/raise引发错误, 然后在defer语句中调用recover()函数捕获错误,这就是Go语言的异常恢复机制——panic-recover机制 两个函数的原型为: func panic(interface{})//接受任意类型参数 无返回值 func recover() interface{}//可以返回任意类型 无参数
一定要记住,你应当把它作为最后的手段来使用,也就是说,你的代码中应当没有,或者很少有panic的东西。这是个强大的工具,请明智地使用 panic() recover() 一般情况下,recover()应该在一个使用defer关键字的函数中执行以有效截取错误处理流程。如果没有在发生异常的goroutine中明确调用恢复 过程(使用recover关键字),会导致该goroutine所属的进程打印异常信息后直接退出。 这里结合自定义的error类型给出一个使用panic和recover的完整例子: package main import ( "fmt" ) //自定义错误类型 type ArithmeticError struct { error } //重写Error()方法 func (this *ArithmeticError) Error() string { return "自定义的error,error名称为算数不合法" } //定义除法运算函数***这里与本文中第一幕①error接口的例子不同 func Devide(num1, num2 int) int { if num2 == 0 { panic(&ArithmeticError{}) //当然也可以使用ArithmeticError{}同时recover等到ArithmeticError类型 } else { return num1 / num2 } } func main() { var a, b int fmt.Scanf("%d %d", &a, &b) defer func() { if r := recover(); r != nil { fmt.Printf("panic的内容%v\n", r) if _, ok := r.(error); ok { fmt.Println("panic--recover()得到的是error类型") } if _, ok := r.(*ArithmeticError); ok { fmt.Println("panic--recover()得到的是ArithmeticError类型") } if _, ok := r.(string); ok { fmt.Println("panic--recover()得到的是string类型") } } }() rs := Devide(a, b) fmt.Println("结果是:", rs) }
其执行的结果为: 使用与上面相同的测试数据,输入5 2得: 5 2 结果是: 2
输入5 0得: 5 0 panic的内容自定义的error,error名称为算数不合法 panic--recover()得到的是error类型 panic--recover()得到的是ArithmeticError类型
可见已将error示例程序转换为了Java中的用法,但是在大多数程序中使用error处理的方法较多。 需要注意的是:defer语句定义的位置 如果defer放在了 rs := Devide(a, b)语句之后,defer将没有机会执行即下面的程序失效: rs := Devide(a, b) defer func() { if r := recover(); r != nil { fmt.Printf("panic的内容%v\n", r) if _, ok := r.(error); ok { fmt.Println("panic--recover()得到的是error类型") } if _, ok := r.(*ArithmeticError); ok { fmt.Println("panic--recover()得到的是ArithmeticError类型") } if _, ok := r.(string); ok { fmt.Println("panic--recover()得到的是string类型") } } }()
因为在在陷入panic之前defer语句没有被加载到内存,而在执行panic时程序被中断,因而无法执行defer语句。
|
请发表评论