异常
大约 4 分钟
异常
网上有吐槽 Go 的异常处理不是很好,原因暂时不知
异常的处理主要使用:
- 内置
- defer
- recover
- errors包
- errors.New
- errors.panic
异常
package main
import "fmt"
func main() {
div(10, 5) // 能执行成功
div(10, 0) // 会报错:panic: runtime error: integer divide by zero
}
func div(num1 int, num2 int) {
result := num1 / num2
fmt.Println(result)
}
异常类型
- 特别需要注意:Go有一种异常为 “panic”,翻译过来叫 “恐慌”,有点怪怪的。这种异常可以通过 recover 恢复(类似其他语言的try-catch)
defer + recover 机制处理错误
defer 基本用法 (在函数中)
与其他语言不同:Go特有。
不同语言的类似处理:
goto方法
- 略
C语言
Setjmp/Longjmp
可以用来实现异常处理和资源释放。setjmp用于捕获当前环境的上下文,而longjmp则用于从setjmp返回,并可以选择性地执行资源释放代码。
这种方法可以在异常发生时跳转到函数的末尾执行清理代码,而不需要使用大量的if语句。
C++
RAII (Resource Acquisition Is Initialization)
与智能指针(如std::unique_ptr或std::shared_ptr)一起使用。当对象被创建时,它会获取资源;当对象被销毁时,它会自动释放资源。
Java的 try-with-resources/try-catch-finally
FileWriter writer = null; try { writer = new FileWriter("example.txt"); // 使用writer对象 // ... } catch (IOException e) { // 处理异常 // ... } finally { if (writer != null) { try { writer.close(); } catch (IOException e) { // 处理关闭资源时的异常 // ... } } }
Python
上下文管理器
__exit__
、__enter__
、with
__exit__
方法定义了退出with
语句块时的行为,通常用于释放资源class MyResource: def __enter__(self): # 获取资源 return self def __exit__(self, exc_type, exc_value, traceback): # 释放资源 if exc_type: # 如果有异常发生,可以选择处理或忽略 print(f"An exception occurred: {exc_type}, {exc_value}") # 总是执行资源释放 # ... with MyResource() as resource: # 使用资源 # ... # 上面是自己定义的情况。一般用得比较多的是打开文件的 open with,无需显式调用 close() 方法
C#
- using语句,类似于Java的try-with-resources
JavaScript
- Promise和async/await
Go语言
- 函数内处理异常:Go使用defer关键字用于处理函数的资源释放,以及异常处理等(关于defer的更多信息,详见Go语言的 “异常” 部分) Go 追求代码优雅,不使用 try-catch 机制,而是使用 defer+recover 机制
- defer关键字:可以在函数返回前执行一段代码,通常用于资源释放。
- recover关键字:允许程序管理恐慌过程。recover() 可以捕获异常,同时使程序恢复正常,停止恐慌过程
- 结合 try-defer 模式,可以在发生异常时自动释放资源,而无需在每个判断点重复编写释放代码
- 函数外处理异常:不使用 “抛出” 异常,由于Go可以多返回值,只需要 “返回” 异常即可
- 函数内处理异常:Go使用defer关键字用于处理函数的资源释放,以及异常处理等(关于defer的更多信息,详见Go语言的 “异常” 部分) Go 追求代码优雅,不使用 try-catch 机制,而是使用 defer+recover 机制
recover
内置函数 recover
作用:允许程序管理恐慌过程。recover()
可以捕获异常,同时使程序恢复正常,停止恐慌过程
defer + recover 处理异常
package main
import "fmt"
func main() {
div(10, 5) // 能执行成功
div(10, 0) // 会报错:panic: runtime error: integer divide by zero
}
func div(num1 int, num2 int) {
// defer+recover 捕获错误
defer func() { // 这里是一个匿名的立刻执行函数
err := recover() // 如果没有捕获错误,返回值为零值:nil
if err != nil {
fmt.Println("错误捕获了, 异常为:", err)
}
}()
result := num1 / num2
fmt.Println(result)
}
异常处理和错误处理
看课看到异常部分时有个弹幕:大哥你这是异常处理不是错误处理,error和panic都没搞清楚。于是我搜了下
- 其他语言:异常处理是通过
try-catch-finally
这样的结构来实现的。例如,在Java或C#中,你可以使用这些关键字来捕获和处理异常 - Go语言:在某些编程语言中,特别是Go语言,
error
和panic
是两种不同的机制,用于处理不同类型的问题- error:是一个接口类型,用于表示错误情况。当函数执行失败时,它通常会返回一个
error
对象,调用者可以检查这个对象来确定是否发生了错误,并获取错误的详细信息 - panic:是一个内置函数,用于在发生严重错误时终止程序的执行。这通常用于那些不应该发生的情况,比如内部一致性被破坏、严重的逻辑错误等。当
panic
被调用时,当前的函数会立即停止执行,跳转到最近的defer
语句(如果有的话),然后程序会打印堆栈跟踪信息并退出。
- error:是一个接口类型,用于表示错误情况。当函数执行失败时,它通常会返回一个
自定义错误 errors包.New()
不同与其他语言:由于Go有多返回值的特性,并不需要像其他语言那样 “抛出” 异常,而仅需 “返回” 异常。这点不错
package main
import (
"fmt"
"errors"
)
func main() {
err := div(10, 5)
if err != nil {
fmt.Println("自定义错误: ", err)
}
err = div(10, 0)
if err != nil {
fmt.Println("自定义错误: ", err)
}
}
func div(num1 int, num2 int) (err error) {
if num2 == 0 {
// 抛出自定义异常
return errors.New("除数不能为0")
} else {
result := num1 / num2
fmt.Println(result)
return nil
}
}
终止程序 errors.panic
有时程序出现错误时,应该立即终止
func panic(v interface{})
// 使用
err := test()
if err != nil {
fmt.Println("自定义错误: ", err)
panic(err)
}