面试题答案
一键面试defer
工作原理
defer
语句用于延迟函数的执行,直到包含该 defer
语句的函数返回。当 defer
语句被执行时,其后面的函数调用会被压入一个栈中。当外层函数执行完毕准备返回时,这些被延迟的函数调用会按照后进先出(LIFO)的顺序依次执行。
recover
工作原理
recover
是一个内置函数,用于在 defer
函数中捕获 panic
。当 panic
发生时,程序会立即停止当前函数的正常执行,开始展开栈(unwind),执行所有被延迟的函数。如果在某个被延迟的函数中调用了 recover
,并且当前存在一个 panic
,那么 recover
会捕获这个 panic
,停止栈的展开,使程序可以继续正常执行。
代码示例
package main
import (
"fmt"
)
func main() {
test()
}
func test() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
fmt.Println("Before panic")
panic("故意引发的 panic")
fmt.Println("After panic") // 这行代码不会被执行
}
在复杂业务逻辑中保证程序稳定性
在复杂业务逻辑中,defer
和 recover
可以防止程序因 panic
而崩溃。通过在关键函数中使用 defer
和 recover
,即使某个子操作发生 panic
,也能确保程序可以捕获异常并进行适当处理,从而维持程序的运行,避免整个系统崩溃。这有助于提高系统的容错性和稳定性,特别是在处理资源管理、事务处理等关键业务场景时。
错误包装(fmt.Errorf
配合 %w
)的作用和优势
- 作用:错误包装允许将一个错误包装在另一个错误中,同时保留原始错误信息。这在多层函数调用中非常有用,因为上层函数可以通过错误包装来传递底层函数返回的错误,同时添加更多上下文信息。
- 优势:
- 保留原始错误信息:通过
%w
格式化动词,原始错误被包含在新的错误中,使得在调试和处理错误时能够获取到完整的错误链。 - 方便错误处理:在调用栈的不同层次,可以通过
errors.Is
和errors.As
函数来检查和提取特定类型的错误,从而进行针对性的处理。这使得错误处理更加灵活和精确,有助于编写健壮的错误处理逻辑。
- 保留原始错误信息:通过
例如:
package main
import (
"errors"
"fmt"
)
var ErrSpecific = errors.New("specific error")
func inner() error {
return ErrSpecific
}
func outer() error {
err := inner()
if err != nil {
return fmt.Errorf("outer function failed: %w", err)
}
return nil
}
func main() {
err := outer()
if err != nil {
if errors.Is(err, ErrSpecific) {
fmt.Println("Caught specific error:", err)
}
}
}
在这个例子中,outer
函数将 inner
函数返回的错误进行了包装,并且在 main
函数中可以通过 errors.Is
函数来判断是否是特定类型的错误。