Go语言学习笔记(五)异常处理

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 欢迎大家访问.提意见.

相关推荐
SimonKing2 小时前
你还在靠重启来调线程池?别人已经做到了实时调控,3分钟接入
java·后端·程序员
IT_陈寒2 小时前
Redis客户端连接池不关闭的后果,程序直接崩给我看
前端·人工智能·后端
可可嘻嘻大老虎2 小时前
SpringBoot拦截器防重复提交实战
java·spring boot·后端
RainCityLucky3 小时前
Java Swing 自定义组件库分享(十一)
java·笔记·后端
cheems95273 小时前
[开发日记]Spring Boot + MyBatis-Plus 抽奖系统排障实录:从 JWT 被拦截到雪花 ID 失控,我是怎样一步步修通登录与人员列表的
spring boot·后端·mybatis
古城小栈3 小时前
Rustix库:Rust 系统编程 的 基石
开发语言·后端·rust
我登哥MVP3 小时前
Spring Boot 从“会用”到“精通”:Rest风格原理
java·spring boot·后端·spring·maven·intellij-idea·mybatis
Je1lyfish3 小时前
CMU15-445 (2025 Fall/2026 Spring) Project#4 - Concurrency Control
开发语言·数据库·c++·笔记·后端·算法·系统架构
我登哥MVP3 小时前
Spring Boot 从“会用”到“精通”:静态资源原理
java·spring boot·后端·spring·tomcat·maven·intellij-idea