1.延迟调用
1.1 关键字defer 用于注册延迟调用。这些调用直到 return 前才被执行,通常⽤用于释放资源或错误处理。
func test() error {
f, err := os.Create(\"test.txt\")
if err != nil { return err }
defer f.Close() // 注册调用,而不是注册函数。必须提供参数,哪怕为空。
f.WriteString(\"Hello, World!\")
return nil
}
1.2 多个 defer 注册,按 FILO 次序执行。哪怕函数或某个延迟调用发生错误,这些调用依旧会被执行。
package main
import \"fmt\"
func test(x int) {
defer fmt.Println(\"a\")
defer fmt.Println(\"b\")
defer func() {
fmt.Println(100 / x) // div0 异常未被捕获,逐步往外传递,最终终⽌止进程。
}()
defer fmt.Println(\"c\")
}
func main() {
test(0)
}
1.3 延迟调用参数在注册时求值或复制,可用指针或闭包 “延迟” 读取。
package main
import \"fmt\"
func test() {
x, y := 10, 20
defer func(i int) {
println(\"defer:\", i, y) // y 闭包引用
}(x) // x 被复制
x += 10
y += 100
fmt.Println(\"x =\", x, \"y =\", y)
}
func main() {
test()
}
1.4 滥用defer可能会导致性能问题,尤其是在一个大循环利。
var lock sync.Mutex
func test() {
lock.Lock()
lock.Unlock()
}
func testdefer() {
lock.Lock()
defer lock.Unlock()
}
func BenchmarkTest(b *testing.B) {
for i := 0; i < b.N; i++ {
test()
}
}
func BenchmarkTestDefer(b *testing.B) {
for i := 0; i < b.N; i++ {
testdefer()
}
}
//输出
BenchmarkTest\" 50000000 43 ns/op
BenchmarkTestDefer 20000000 128 ns/op
2. 错误处理
2.1 没有结果化异常,使用panic抛出错误,recover捕获错误。
func test() {
defer func() {
if err := recover(); err != nil {
println(err.(string)) // 将 interface{} 转型为具体类型。
}
}()
panic(\"panic error!\")
}
2.2 由于 panic、recover 参数类型为 interface{},因此可抛出任何类型对象。
func panic(v interface{})
func recover() interface{}
2.3 延迟调用中引发的错误,可被后续延迟调用捕获,但仅最后一个错误可被捕获。
func test() {
defer func() {
fmt.Println(recover())
}()
defer func() {
panic(\"defer panic\")
}()
panic(\"test panic\")
}
func main() {
test()
}
//输出:
defer panic
2.4 捕获函数 recover 只有在延迟调用内直接调用才会终止错误,否则总是返回 nil。任何未捕获的错误都会沿调用堆栈向外传递。
func test() {
defer recover() // 无效!
defer fmt.Println(recover()) // 无效!
defer func() {
func() {
println(\"defer inner\")
recover() // 无效!
}()
}()
panic(\"test panic\")
}
func main() {
test()
}
//输出:
defer inner
<nil>
panic: test panic
2.5 使用延迟匿名函数或者下面这样都是有效的。
func except() {
recover()
}
func test() {
defer except()
panic(\"test panic\")
}
2.6 如果需要保护代码片段,可将代码块重构成匿名函数,如此可确保后续代码被执行。
func test(x, y int) {
var z int
func() {
defer func() {
if recover() != nil { z = 0 }
}()
z = x / y
return
}()
println(\"x / y =\", z)
}
2.7 除用 panic 引发中断性错误外,还可返回 error 类型错误对象来表示函数调用状态。
type error interface {
Error() string
}
2.8 标准库 errors.New 和 fmt.Errorf 函数用于创建实现 error 接口的错误对象。通过判断错误对象实例来确定具体错误类型。
package main
import (
\"errors\"
\"fmt\"
)
var ErrDivByZero = errors.New(\"division by zero\")
func div(x, y int) (int, error) {
if y == 0 {
return 0, ErrDivByZero
}
return x / y, nil
}
func main() {
switch z, err := div(10, 0); err {
case nil:
fmt.Println(z)
case ErrDivByZero:
panic(err)
}
}
2.9 如何区别使用 panic 和 error 两种方式?惯例是:导致关键流程出现不可修复性错误的使用 panic,其他使用 error。
继续阅读与本文标签相同的文章
下一篇 :
java获取每年所有月份的的周六日的日期
-
吉利缤越,液晶仪表盘,运动座椅,L2级别自动驾驶,8秒破百
2026-05-18栏目: 教程
-
苏泊尔破壁机:技术革新 家族合力
2026-05-18栏目: 教程
-
OpenStack Train版本今日正式发布并开放下载
2026-05-18栏目: 教程
-
文在寅:八年后将韩国打造成全球第一个自动驾驶国家
2026-05-18栏目: 教程
-
Android Studio运行Hello World程序
2026-05-18栏目: 教程
