1.异常机制:
异常就是程序运行的时候出现的问题导致程序必须中断运行.决定了程序的执行走向.异常与普通逻辑是一种比较激进的处理方式.
2.Go语言异常:
2.1创建异常:
go
import "errors"
type error interface {
Error() string
}
func errorTest() {
err := errors.New("出错啦.")
}
errors是包名,New是包中的函数.
2.2New源码:


errorString包含了一个s的字符串字段,实现了Error()方法.
3.抛出异常:
go
package main
import (
"errors"
"fmt"
)
func main() {
fmt.Println("开始执行")
handle()
}
func handle() {
panic(errors.New("抛出异常."))
}
3.1执行结果:

4.自定义异常:
4.1自定义函数基本用法:


从源码可以看出,any是一个空接口.注释可以得知,any是一个别名.可以自定义一个异常通过panic抛出.
4.2自定义异常实现:
go
package main
import (
"errors"
"fmt"
)
func main() {
fmt.Println("开始执行")
//handle()
handleCustomError()
}
type CustomError struct {
//错误码.
Code int
//错误消息
Msg string
}
func handleCustomError() {
customError := CustomError{502, "接口无效"}
//抛出异常
panic(&customError)
}
4.3执行结果:

输出自定义异常的实例也可以达到中断程序的效果.而且这个异常也没有任何特殊之处.关键还是在于panic函数.
4.4自定义异常实现error接口:
go
func (e *CustomError) Error() string {
return strconv.Itoa(e.Code) + " " + e.Msg
}
4.5执行结果:

5.异常捕获:
异常捕获可以让异常不在向上层调用者传播.程序便不会中断.
5.1recover捕获异常错误使用:
go
func handleError() {
panic(errors.New("出错了"))
if r := recover(); r != nil {
fmt.Printf("%+v\n", r)
}
}
5.2执行结果:

因为panic会中断程序,导致recover没办法执行.
5.3defer指令延迟捕获异常:
go
func catchError() {
defer func() {
if r := recover(); r != nil {
fmt.Printf("%+v\n", r)
}
}()
panic(errors.New("出错了"))
}
5.4执行结果:

从结果可以看出异常被捕获了.
5.5上层捕获异常:
go
package main
import (
"errors"
"fmt"
"strconv"
)
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Printf("%+v\n", r)
}
}()
fmt.Println("开始执行")
panicError()
fmt.Println("结束执行")
}
func panicError() {
panic(errors.New("出错了"))
}
5.6执行结果:

并没有执行异常后的逻辑.
5.7父协程无法捕获子协程异常:
go
package main
import (
"errors"
"fmt"
"strconv"
"time"
)
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Printf("%+v\n", r)
}
}()
fmt.Println("开始执行")
go func() {
panicError()
}()
time.Sleep(1 * time.Second)
fmt.Println("结束执行")
}
func panicError() {
panic(errors.New("出错了"))
}
延迟一秒,让子协程可以执行.
5.8执行结果:

5.9不正确异常捕获资源清理:
go
package main
import (
"errors"
"fmt"
"strconv"
"sync"
)
// 定义等待组对象.
var wg = sync.WaitGroup{}
// 定义结构体
type safeData struct {
content map[int]int
lock sync.RWMutex
}
// 添加数据
func (m *safeData) addEntry(k int) {
//延迟处理.
defer func() {
//异常捕获
if err := recover(); err != nil {
fmt.Printf("出错啦,k=%d,err=%v\n", k, err)
} else {
fmt.Println("已添加,k=", k)
}
wg.Done()
}()
m.lock.Lock()
m.content[k] = k / (k - 10)
//释放锁
m.lock.Unlock()
}
func main() {
//等待组计数器加10.
wg.Add(10)
data := safeData{}
data.content = make(map[int]int, 10)
//循环10次.启动子协程.
for i := range 11 {
go func(i int) {
data.addEntry(i)
}(i)
}
wg.Wait()
}
5.9.1执行结果:

会导致释放锁的操作未执行.
scss
// 添加数据
func (m *safeData) addEntry(k int) {
//延迟处理.
defer func() {
//异常捕获
if err := recover(); err != nil {
fmt.Printf("出错啦,k=%d,err=%v\n", k, err)
} else {
fmt.Println("已添加,k=", k)
}
wg.Done()
}()
m.lock.Lock()
m.content[k] = k / (k - 10)
//保证释放锁
defer m.lock.Unlock()
}
这样就会保证锁释放.
语雀地址www.yuque.com/itbosunmian...?
《Go.》 密码:xbkk 欢迎大家访问.提意见.