跳到主要内容

Go defer, panic和recover

提示
  1. defer 延迟执行: defer 用于将函数调用延迟到其他函数执行完毕后再执行,常用于处理可能引发错误的操作。
  2. panic 立即终止程序: panic 用于在遇到无法恢复的严重错误时立即终止程序,后续代码不会执行。
  3. recover 恢复异常: recover 用于处理panic引发的程序中断,允许程序从异常状态恢复并继续执行。

在 Go 中,我们使用 deferpanicrecover 语句来处理错误。

我们使用 defer 来延迟可能会导致错误的函数的执行。panic 语句会立即终止程序,而 recover 用于在 panic 期间恢复消息。

在我们学习错误处理语句之前,请确保了解 Go 错误

Go 中的 defer

我们使用 defer 语句来阻止函数的执行,直到所有其他函数执行完毕。例如,

package main
import "fmt"

func main() {

// 延迟执行 Println() 函数
defer fmt.Println("Three")

fmt.Println("One")
fmt.Println("Two")

}

输出

One
Two
Three

在上述示例中,我们在第一个打印语句之前使用了 defer。

这就是为什么 Println() 函数在所有其他函数执行完毕后最后执行。

Go 中的多个 defer 语句

当我们在一个程序中使用多个 defer 时,defer 语句的执行顺序将是 LIFO(后进先出)

这意味着最后的 defer 语句将首先被执行。例如,

package main
import "fmt"

func main() {

defer fmt.Println("One")
defer fmt.Println("Two")
defer fmt.Println("Three")

}

输出

Three
Two
One

在上述示例中,我们使用 3 个 defer 语句调用了 Println() 函数。

正如你所看到的,执行顺序是 LIFO。也就是说,最后的 defer 语句首先执行,而第一个 defer 语句最后执行。

Golang 中的 panic

我们使用 panic 语句来立即结束程序的执行。如果我们的程序因一些重大错误达到无法恢复的地步,最好使用 panic。

panic 语句之后的代码行不会被执行。例如,

package main
import "fmt"

func main() {

fmt.Println("帮助!发生了糟糕的事情。")
panic("终止程序")
fmt.Println("等待执行")

}

输出

帮助!发生了糟糕的事情。
panic: 终止程序

这里,当程序执行到 panic 语句时就终止了。这就是为什么 panic 之后的打印语句没有执行。

注意: 输出中的 panic: 指明程序因 panic 而终止,它是 panic 消息。

示例:Golang 中的 Panic

package main

import "fmt"

func division(num1, num2 int) {

// 如果 num2 为 0,则因 panic 而终止程序
if num2 == 0 {
panic("不能除以零")
} else {
result := num1 / num2
fmt.Println("结果: ", result)
}
}

func main() {

division(4, 2)
division(8, 0)
division(2, 8)

}

输出

结果: 2
panic: 不能除以零

在上述示例中,我们创建了一个函数,用于执行两个数字的除法:num1num2

在函数内部,我们使用了 if...else 语句 检查 num2(除数)是否为 0。如果是 0,由于 panic 语句,程序的执行将停止。

panic("不能除以零")

在这里,当我们运行程序时,首先得到结果 2(4 除以 2)。然而,在第二次函数调用时,num2 的值是 0(8 除以 0)。

因此,程序的执行被终止。

Go 编程中的 recover

panic 语句会立即终止程序。然而,有时候对于程序来说完成执行并得到一些所需结果是很重要的。

在这种情况下,我们使用 recover 语句来处理 Go 中的 panic。recover 语句阻止程序的终止,并从 panic 中恢复程序。

让我们看一个示例。

package main
import "fmt"

// 处理 panic 的 recover 函数
func handlePanic() {

// 检测是否发生了 panic
a := recover()

if a != nil {
fmt.Println("RECOVER", a)
}

}

func division(num1, num2 int) {

// 即使在 panic 发生后也执行 handlePanic
defer handlePanic()

// 如果 num2 为 0,则因 panic 而终止程序
if num2 == 0 {
panic("不能除以零")
} else {
result := num1 / num2
fmt.Println("结果: ", result)
}
}

func main() {

division(4, 2)
division(8, 0)
division(2, 8)

}

输出

结果: 2
RECOVER 不能除以零
结果: 0

在上述示例中,我们创建了一个 handlePanic() 函数来从慌乱状态中恢复。

func handlePanic() {

// 检测是否发生了 panic
a := recover()

if a != nil {
fmt.Println("RECOVER", a)
}
}

这里,我们使用 a := recover() 来检测程序中是否发生了 panic 并将 panic 消息赋给 a。

在我们的示例中,发生了 panic,所以 a 中会有一些值。因此,执行了 if 语句,它打印了 panic 消息。

division() 函数中,我们调用了 handlePanic() 函数

defer handlePanic()

这里,请注意两件事:

  • **我们在 panic 发生之前调用了 handlePanic()。**这是因为如果程序遇到 panic,将会被终止,我们希望在终止之前执行 handlePanic()
  • **我们使用 defer 来调用 handlePanic()。**这是因为我们只想在 panic 发生后处理它,所以我们推迟了它的执行。