投资网站策划,网站logo在线设计,成品网站模板下载,内蒙古呼和浩特网站建设错误处理
错误处理是编程中用于识别、响应和恢复程序运行时出现的错误和异常情况的过程。其目的是确保程序的鲁棒性#xff08;一个系统、模型或函数在面对错误输入、工作压力、意外情况或故意攻击时仍能保持稳定性和可靠性的能力#xff09;#xff0c;即使在出现错误的情…错误处理
错误处理是编程中用于识别、响应和恢复程序运行时出现的错误和异常情况的过程。其目的是确保程序的鲁棒性一个系统、模型或函数在面对错误输入、工作压力、意外情况或故意攻击时仍能保持稳定性和可靠性的能力即使在出现错误的情况下也能正常运行或优雅地终止。
而Go语言中的错误处理是一种显式的、基于值的机制与许多其他编程语言使用异常exception不同。Go通过返回值来处理错误这使得代码更加清晰和可预测。
基本概念
error接口 Go内置了一个名为error的接口用于表示错误。源码 // The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
// 内置的error接口类型是表示错误条件的常规接口其nil值代表没有错误。
type error interface {Error() string
}任何实现了这个接口的方法都可以作为一个错误对象。
自定义error 使用errors.New或fmt.Errorf()创建简单的文本描述性错误。 func Test1(t *testing.T) {err1 : errors.New(自定义错误1)fmt.Println(err1.Error())err2 : fmt.Errorf(自定义错误2)fmt.Println(err2)
}输出 自定义错误1
自定义错误2如果需要更复杂或结构化的信息可以定义自己的类型并实现Error()方法。 type MyError struct {code intmsg string
}func (m *MyError) Error() string {return fmt.Sprintf(code%d,msg:%s, m.code, m.msg)
}func Test2(t *testing.T) {myError : MyError{404,自定义错误,}fmt.Println(myError.Error())fmt.Println(myError)}输出 code404,msg:自定义错误
{404 自定义错误}函数返回值 在Go中函数通常会返回两个值结果和一个表示可能发生的错误。 func testDivide(a float64, b float64) (float64, error) {if b 0 {return 0, fmt.Errorf(分母不能为0)} else {return a / b, nil}}检查和处理错误
调用函数时需要检查第二个返回值是否为非nil以判断是否发生了错误。
func Test3(t *testing.T) {var a float64 1var b float64divide, err : testDivide(a, b)if err ! nil {fmt.Println(divide, err.Error())} else {fmt.Printf(结果为%.2f\n, divide)}}输出
0 分母不能为0错误包装与解包 Go1.13引入了对error wrapping的支持通过使用 fmt.Errorf() 和 %w 格式化动词可以将上下文信息附加到原始错误上。 使用 errors.Unwrap() 可以提取被包装过的一层的错误。 errors.Is函数用于检查一个错误是否与目标错误相同或者是否是目标错误的一部分。
func Test4(t *testing.T) {err1 : errors.New(第一层错误)err2 : fmt.Errorf(第二层%w, err1)fmt.Println(err1)fmt.Println(err2)// errors.Is函数用于检查一个错误是否与目标错误相同或者是否是目标错误的一部分。// 注意参数顺序检查第一个错误是不是第二个错误的子错误或是否同一个错误bool1 : errors.Is(err1, err2) // 返回falsefmt.Println(bool1)bool2 : errors.Is(err2, err1)fmt.Println(bool2)err3 : errors.Unwrap(err2)fmt.Println(err3)
}输出
第一层错误
第二层第一层错误
false
true
第一层错误错误类型断言与比较
使用类型断言来判断特定类型的自定义异常并访问其内部字段。推荐errors.As函数用于检查一个错误链中是否存在某个特定类型的错误并且可以将该错误赋值给目标变量。它不仅适用于简单的类型断言还能处理被包装过的复杂错误结构。
func Test5(t *testing.T) {myError : MyError{code: 400,msg: 自定义错误,}var myErr error myError// 假设我们只知道这是个错误但不知道具体的类型时可以这样处理if err, ok : myErr.(*MyError); ok {fmt.Println(err.code, err.msg)}// 但是官方还是推荐我们使用as来判断具体的错误类型var targetErr *MyErrorif errors.As(myErr, targetErr) {fmt.Println(targetErr.code, targetErr.msg)}}输出
400 自定义错误
400 自定义错误断言只能判断是否那个类型而error.As能查找是否该类型或子类型
func Test6(t *testing.T) {myError : MyError{code: 400,msg: 自定义错误,}var myErr error myError// 再封装一层var myErrNew fmt.Errorf(封装多一层%w, myErr)// 假设我们只知道这是个错误但不知道具体的类型时可以这样处理if err, ok : myErrNew.(*MyError); ok {fmt.Println(1:, err.code, err.msg)}// 但是官方还是推荐我们使用as来判断具体的错误类型var targetErr *MyErrorif errors.As(myErrNew, targetErr) {fmt.Println(2:, targetErr.code, targetErr.msg)}}输出
2: 400 自定义错误panic和recover
panic
panic是Go语言中的一种内建机制用于表示程序遇到了无法恢复的严重错误导致程序的正常执行流程被中断。它类似于其他编程语言中的异常但在Go中主要用于不可恢复的错误情况。虽然不推荐在正常业务逻辑中使用 panic但在一些无法恢复或者必须立即停止程序执行情况下可以考虑比如数组越界等。
关键特点 立即停止控制流 当panic被调用时程序会立即停止当前函数的执行并开始运行任何已注册的defer函数。 向上传播 panic会沿着调用栈向上传播逐层退出每个调用函数并在每个退出点运行相应的defer语句。 终止程序 如果没有捕获到并处理这个panic即没有使用recover最终会导致整个程序崩溃并打印出堆栈跟踪信息。
使用场景
通常用于那些不应该发生、且无法继续安全运行下去的问题例如数组越界、空指针解引用等。不建议在普通业务逻辑中使用而是保留给真正需要立即停止执行的问题。
func Test1(t *testing.T) {defer fmt.Println(执行defer)fmt.Println(程序正常执行)panic(遇到错误)fmt.Println(程序继续执行)}输出 RUN Test1
程序正常执行
执行defer
--- FAIL: Test1 (0.00s)
panic: 遇到错误 [recovered]panic: 遇到错误goroutine 6 [running]:
testing.tRunner.func1.2({0xa25ade0, 0xa284890}).........recover
recover是Go语言中用于处理panic的一种机制。它允许程序在发生panic后恢复执行从而避免程序崩溃。通常与defer一起使用以确保即使在函数发生恐慌时也能执行一些清理操作。
基本用法
捕获panic(恐慌)当一个函数调用了 panic() 时程序会立即停止当前函数的执行并开始沿着调用栈向上传播直到遇到一个能够处理该panic的地方。如果在传播过程中遇到了一个包含 recover() 的延迟deferred函数调用那么可以通过 recover() 捕获这个恐慌。返回值如果成功捕获到一个正在传播中的恐慌recover() 会返回传递给该 panic() 调用的值如果没有处于panic状态则返回 nil。
使用场景
防止程序崩溃通过捕获和处理不可预见或致命错误使得程序能够继续运行或进行适当降级。清理资源确保无论是否出现错误都能正确释放资源如文件句柄、网络连接等。
func Test2(t *testing.T) {defer func() {a : recover()if a ! nil {fmt.Println(recover捕获到panic, a)} else {fmt.Println(recover没有捕获到panic)}}()fmt.Println(程序正常执行)panic(遇到错误)fmt.Println(程序继续执行)}输出
程序正常执行
recover捕获到panic 遇到错误和throw相似
学过其他语言特别是java的同学应该发现这与Java中的throw关键字非常相似。
尽管panic和throw在概念上相似但它们在各自的语言中有着不同的使用习惯和语义。在Go中panic通常用于处理真正的异常情况比如程序的内部错误而错误处理则通过返回error类型来完成。在Java中throw关键字则用于更广泛的场景包括业务逻辑中预期的异常情况。 主动抛出异常在Go中使用panic和在Java中使用throw都可以主动抛出一个异常或错误条件。 改变程序控制流两者都会导致程序的正常控制流被改变。在Go中panic会立即停止当前函数的执行并开始执行defer语句然后向上返回直到被recover捕获或者程序终止。而在Java中如果没有在该方法内部捕获则需要由上层调用者负责处理否则可能会导致程序崩溃。 异常传播在两个语言中如果抛出的异常没有被立即捕获它会继续向上传播到调用栈中的更高层直到被捕获或者导致程序终止。 用于不可恢复的错误panic和throw都用于表示那些不应该被忽略的严重错误它们通常用于指示程序遇到了不可恢复的状态。 停止执行后续代码在panic或throw之后的代码将不会被执行直到异常被处理。 堆栈跟踪当panic发生时Go会打印出堆栈跟踪信息类似于Java中throw异常时的行为这有助于开发者定位错误发生的位置。