/* 03-panic-recover.go - Go 语言 Panic 和 Recover 详解 学习目标: 1. 理解 panic 的概念和使用场景 2. 掌握 recover 的恢复机制 3. 学会 defer 与 panic/recover 的配合 4. 了解 panic 的传播机制 5. 掌握 panic/recover 的最佳实践 知识点: - panic 的触发条件和行为 - recover 的恢复机制 - defer 语句的执行顺序 - panic 的传播和栈展开 - panic/recover 的实际应用 - 错误处理 vs panic 的选择 */ package main import ( "fmt" "runtime" "strconv" "time" ) func main() { fmt.Println("=== Go 语言 Panic 和 Recover 详解 ===\\n") // 演示 panic 的基本概念 demonstratePanicBasics() // 演示 recover 的使用 demonstrateRecover() // 演示 defer 与 panic/recover 的配合 demonstrateDeferWithPanicRecover() // 演示 panic 的传播 demonstratePanicPropagation() // 演示 panic/recover 的实际应用 demonstratePracticalUsage() // 演示最佳实践 demonstrateBestPractices() // 演示错误处理 vs panic 的选择 demonstrateErrorVsPanic() } // demonstratePanicBasics 演示 panic 的基本概念 func demonstratePanicBasics() { fmt.Println("1. Panic 的基本概念:") // panic 的基本特性 fmt.Printf(" Panic 的基本特性:\\n") fmt.Printf(" - panic 是运行时错误,会导致程序崩溃\\n") fmt.Printf(" - panic 会停止当前函数的执行\\n") fmt.Printf(" - panic 会执行当前函数的 defer 语句\\n") fmt.Printf(" - panic 会向上传播到调用栈\\n") fmt.Printf(" - panic 可以被 recover 捕获\\n") // 手动触发 panic fmt.Printf(" 手动触发 panic:\\n") func() { defer func() { if r := recover(); r != nil { fmt.Printf(" 捕获到 panic: %v\\n", r) } }() fmt.Printf(" 准备触发 panic\\n") panic("这是一个手动触发的 panic") fmt.Printf(" 这行代码不会执行\\n") }() // 常见的 panic 场景 fmt.Printf(" 常见的 panic 场景:\\n") // 1. 数组越界 func() { defer func() { if r := recover(); r != nil { fmt.Printf(" 数组越界 panic: %v\\n", r) } }() arr := [3]int{1, 2, 3} fmt.Printf(" 访问数组索引 5: %d\\n", arr[5]) // 会 panic }() // 2. 空指针解引用 func() { defer func() { if r := recover(); r != nil { fmt.Printf(" 空指针解引用 panic: %v\\n", r) } }() var ptr *int fmt.Printf(" 解引用空指针: %d\\n", *ptr) // 会 panic }() // 3. 类型断言失败 func() { defer func() { if r := recover(); r != nil { fmt.Printf(" 类型断言失败 panic: %v\\n", r) } }() var x interface{} = "hello" num := x.(int) // 会 panic fmt.Printf(" 类型断言结果: %d\\n", num) }() // 4. 向已关闭的 channel 发送数据 func() { defer func() { if r := recover(); r != nil { fmt.Printf(" 向已关闭 channel 发送数据 panic: %v\\n", r) } }() ch := make(chan int) close(ch) ch <- 1 // 会 panic }() fmt.Println() } // demonstrateRecover 演示 recover 的使用 func demonstrateRecover() { fmt.Println("2. Recover 的使用:") // recover 的基本概念 fmt.Printf(" Recover 的基本概念:\\n") fmt.Printf(" - recover 只能在 defer 函数中使用\\n") fmt.Printf(" - recover 可以捕获当前 goroutine 的 panic\\n") fmt.Printf(" - recover 返回 panic 的值\\n") fmt.Printf(" - recover 后程序可以继续执行\\n") // 基本 recover 示例 fmt.Printf(" 基本 recover 示例:\\n") result := safeFunction(func() int { panic("计算过程中发生错误") return 42 }) fmt.Printf(" 安全函数返回: %d\\n", result) // recover 的返回值 fmt.Printf(" recover 的返回值:\\n") func() { defer func() { if r := recover(); r != nil { fmt.Printf(" panic 值: %v\\n", r) fmt.Printf(" panic 类型: %T\\n", r) // 根据 panic 类型进行不同处理 switch v := r.(type) { case string: fmt.Printf(" 字符串 panic: %s\\n", v) case int: fmt.Printf(" 整数 panic: %d\\n", v) case error: fmt.Printf(" 错误 panic: %v\\n", v) default: fmt.Printf(" 其他类型 panic: %v\\n", v) } } }() // 触发不同类型的 panic testPanics := []interface{}{ "字符串错误", 42, fmt.Errorf("错误对象"), struct{ Message string }{"结构体错误"}, } for i, panicValue := range testPanics { func() { defer func() { if r := recover(); r != nil { fmt.Printf(" 测试 %d - panic: %v (%T)\\n", i+1, r, r) } }() panic(panicValue) }() } }() // recover 的条件 fmt.Printf(" recover 的条件:\\n") // 正确的 recover 使用 func() { defer func() { if r := recover(); r != nil { fmt.Printf(" 正确捕获 panic: %v\\n", r) } }() panic("正确的 recover 示例") }() // 错误的 recover 使用 func() { defer func() { // 这个 recover 不会捕获到 panic,因为不是直接在 defer 中调用 go func() { if r := recover(); r != nil { fmt.Printf(" 错误的 recover: %v\\n", r) } }() }() defer func() { if r := recover(); r != nil { fmt.Printf(" 正确捕获嵌套 panic: %v\\n", r) } }() panic("嵌套 panic 示例") }() fmt.Println() } // demonstrateDeferWithPanicRecover 演示 defer 与 panic/recover 的配合 func demonstrateDeferWithPanicRecover() { fmt.Println("3. Defer 与 Panic/Recover 的配合:") // defer 的执行顺序 fmt.Printf(" defer 的执行顺序:\\n") func() { defer fmt.Printf(" defer 1\\n") defer fmt.Printf(" defer 2\\n") defer func() { if r := recover(); r != nil { fmt.Printf(" recover: %v\\n", r) } }() defer fmt.Printf(" defer 3\\n") fmt.Printf(" 正常执行\\n") panic("测试 defer 顺序") fmt.Printf(" 这行不会执行\\n") }() // 多层 defer 和 recover fmt.Printf(" 多层 defer 和 recover:\\n") func() { defer func() { fmt.Printf(" 外层 defer 开始\\n") if r := recover(); r != nil { fmt.Printf(" 外层 recover: %v\\n", r) } fmt.Printf(" 外层 defer 结束\\n") }() defer func() { fmt.Printf(" 内层 defer 开始\\n") defer func() { if r := recover(); r != nil { fmt.Printf(" 内层 recover: %v\\n", r) } }() fmt.Printf(" 内层 defer 结束\\n") }() panic("多层 defer 测试") }() // defer 中的资源清理 fmt.Printf(" defer 中的资源清理:\\n") processWithCleanup := func() { // 模拟资源分配 resource := "重要资源" fmt.Printf(" 分配资源: %s\\n", resource) defer func() { // 资源清理 fmt.Printf(" 清理资源: %s\\n", resource) // 捕获 panic if r := recover(); r != nil { fmt.Printf(" 处理过程中发生 panic: %v\\n", r) fmt.Printf(" 资源已安全清理\\n") } }() // 模拟可能 panic 的操作 if time.Now().UnixNano()%2 == 0 { panic("随机 panic") } fmt.Printf(" 正常处理完成\\n") } // 执行多次以演示不同情况 for i := 0; i < 3; i++ { fmt.Printf(" 执行 %d:\\n", i+1) processWithCleanup() } fmt.Println() } // demonstratePanicPropagation 演示 panic 的传播 func demonstratePanicPropagation() { fmt.Println("4. Panic 的传播:") // panic 的栈展开 fmt.Printf(" panic 的栈展开:\\n") func() { defer func() { if r := recover(); r != nil { fmt.Printf(" 最终捕获 panic: %v\\n", r) // 打印调用栈 buf := make([]byte, 1024) n := runtime.Stack(buf, false) fmt.Printf(" 调用栈:\\n%s\\n", buf[:n]) } }() level1() }() // panic 的中途拦截 fmt.Printf(" panic 的中途拦截:\\n") func() { defer func() { if r := recover(); r != nil { fmt.Printf(" 最外层捕获: %v\\n", r) } }() interceptedLevel1() }() // panic 的重新抛出 fmt.Printf(" panic 的重新抛出:\\n") func() { defer func() { if r := recover(); r != nil { fmt.Printf(" 最终处理 panic: %v\\n", r) } }() rethrowLevel1() }() fmt.Println() } // demonstratePracticalUsage 演示 panic/recover 的实际应用 func demonstratePracticalUsage() { fmt.Println("5. Panic/Recover 的实际应用:") // Web 服务器中的 panic 恢复 fmt.Printf(" Web 服务器中的 panic 恢复:\\n") requests := []string{ "/api/users", "/api/panic", "/api/orders", "/api/error", } for _, path := range requests { handleHTTPRequest(path) } // 工作池中的 panic 处理 fmt.Printf(" 工作池中的 panic 处理:\\n") jobs := []Job{ {ID: 1, Data: "正常任务"}, {ID: 2, Data: "panic任务"}, {ID: 3, Data: "正常任务"}, {ID: 4, Data: "error任务"}, } for _, job := range jobs { processJobSafely(job) } // 数据解析中的 panic 处理 fmt.Printf(" 数据解析中的 panic 处理:\\n") data := []string{ "123", "abc", "456", "", "789", } for _, item := range data { result := parseIntSafely(item) fmt.Printf(" 解析 '%s': %d\\n", item, result) } // 递归函数中的 panic 处理 fmt.Printf(" 递归函数中的 panic 处理:\\n") testValues := []int{5, -1, 10, 0} for _, n := range testValues { result := safeFactorial(n) fmt.Printf(" %d! = %d\\n", n, result) } fmt.Println() } // demonstrateBestPractices 演示最佳实践 func demonstrateBestPractices() { fmt.Println("6. 最佳实践:") // 最佳实践原则 fmt.Printf(" 最佳实践原则:\\n") fmt.Printf(" 1. 优先使用错误返回值而不是 panic\\n") fmt.Printf(" 2. 只在真正异常的情况下使用 panic\\n") fmt.Printf(" 3. 在库代码中避免 panic,转换为错误\\n") fmt.Printf(" 4. 在应用边界使用 recover 保护\\n") fmt.Printf(" 5. 记录 panic 信息用于调试\\n") // 库函数的 panic 处理 fmt.Printf(" 库函数的 panic 处理:\\n") // 不好的做法:库函数直接 panic fmt.Printf(" 不好的做法示例:\\n") result, err := callLibraryFunction("invalid") if err != nil { fmt.Printf(" 库函数错误: %v\\n", err) } else { fmt.Printf(" 库函数结果: %s\\n", result) } // 好的做法:将 panic 转换为错误 fmt.Printf(" 好的做法示例:\\n") result, err = safeLibraryFunction("invalid") if err != nil { fmt.Printf(" 安全库函数错误: %v\\n", err) } else { fmt.Printf(" 安全库函数结果: %s\\n", result) } // panic 信息的记录 fmt.Printf(" panic 信息的记录:\\n") func() { defer func() { if r := recover(); r != nil { logPanic(r) } }() panic("需要记录的 panic") }() // 优雅的服务关闭 fmt.Printf(" 优雅的服务关闭:\\n") server := &Server{name: "测试服务器"} server.Start() // 模拟服务运行中的 panic func() { defer server.handlePanic() panic("服务运行时错误") }() server.Stop() fmt.Println() } // demonstrateErrorVsPanic 演示错误处理 vs panic 的选择 func demonstrateErrorVsPanic() { fmt.Println("7. 错误处理 vs Panic 的选择:") // 使用错误的场景 fmt.Printf(" 使用错误的场景:\\n") fmt.Printf(" - 预期可能发生的错误\\n") fmt.Printf(" - 用户输入错误\\n") fmt.Printf(" - 网络或I/O错误\\n") fmt.Printf(" - 业务逻辑错误\\n") // 使用 panic 的场景 fmt.Printf(" 使用 panic 的场景:\\n") fmt.Printf(" - 程序逻辑错误\\n") fmt.Printf(" - 不可恢复的错误\\n") fmt.Printf(" - 初始化失败\\n") fmt.Printf(" - 断言失败\\n") // 对比示例 fmt.Printf(" 对比示例:\\n") // 错误处理示例 fmt.Printf(" 错误处理示例:\\n") result, err := divide(10, 0) if err != nil { fmt.Printf(" 除法错误: %v\\n", err) } else { fmt.Printf(" 除法结果: %.2f\\n", result) } // panic 示例 fmt.Printf(" panic 示例:\\n") func() { defer func() { if r := recover(); r != nil { fmt.Printf(" 捕获 panic: %v\\n", r) } }() assertPositive(-5) }() // 混合使用示例 fmt.Printf(" 混合使用示例:\\n") calculator := &Calculator{} // 正常计算 result, err = calculator.Calculate("10 + 5") if err != nil { fmt.Printf(" 计算错误: %v\\n", err) } else { fmt.Printf(" 计算结果: %.2f\\n", result) } // 无效表达式 result, err = calculator.Calculate("invalid") if err != nil { fmt.Printf(" 计算错误: %v\\n", err) } else { fmt.Printf(" 计算结果: %.2f\\n", result) } // 内部 panic 被转换为错误 result, err = calculator.Calculate("panic") if err != nil { fmt.Printf(" 计算错误: %v\\n", err) } else { fmt.Printf(" 计算结果: %.2f\\n", result) } fmt.Println() } // ========== 辅助函数和类型定义 ========== // safeFunction 安全执行函数 func safeFunction(fn func() int) int { defer func() { if r := recover(); r != nil { fmt.Printf(" 函数执行中发生 panic,已恢复: %v\\n", r) } }() return fn() } // level1 第一层函数 func level1() { defer func() { fmt.Printf(" level1 defer 执行\\n") }() fmt.Printf(" 进入 level1\\n") level2() fmt.Printf(" level1 正常结束\\n") } // level2 第二层函数 func level2() { defer func() { fmt.Printf(" level2 defer 执行\\n") }() fmt.Printf(" 进入 level2\\n") level3() fmt.Printf(" level2 正常结束\\n") } // level3 第三层函数 func level3() { defer func() { fmt.Printf(" level3 defer 执行\\n") }() fmt.Printf(" 进入 level3\\n") panic("level3 中的 panic") fmt.Printf(" level3 正常结束\\n") } // interceptedLevel1 中途拦截的第一层 func interceptedLevel1() { defer func() { fmt.Printf(" interceptedLevel1 defer\\n") }() fmt.Printf(" 进入 interceptedLevel1\\n") interceptedLevel2() fmt.Printf(" interceptedLevel1 正常结束\\n") } // interceptedLevel2 中途拦截的第二层 func interceptedLevel2() { defer func() { if r := recover(); r != nil { fmt.Printf(" interceptedLevel2 拦截 panic: %v\\n", r) // 不重新抛出,panic 在这里被处理 } fmt.Printf(" interceptedLevel2 defer\\n") }() fmt.Printf(" 进入 interceptedLevel2\\n") interceptedLevel3() fmt.Printf(" interceptedLevel2 正常结束\\n") } // interceptedLevel3 中途拦截的第三层 func interceptedLevel3() { defer func() { fmt.Printf(" interceptedLevel3 defer\\n") }() fmt.Printf(" 进入 interceptedLevel3\\n") panic("interceptedLevel3 中的 panic") fmt.Printf(" interceptedLevel3 正常结束\\n") } // rethrowLevel1 重新抛出的第一层 func rethrowLevel1() { defer func() { fmt.Printf(" rethrowLevel1 defer\\n") }() fmt.Printf(" 进入 rethrowLevel1\\n") rethrowLevel2() fmt.Printf(" rethrowLevel1 正常结束\\n") } // rethrowLevel2 重新抛出的第二层 func rethrowLevel2() { defer func() { if r := recover(); r != nil { fmt.Printf(" rethrowLevel2 捕获并重新抛出 panic: %v\\n", r) panic(fmt.Sprintf("重新抛出: %v", r)) // 重新抛出 } fmt.Printf(" rethrowLevel2 defer\\n") }() fmt.Printf(" 进入 rethrowLevel2\\n") rethrowLevel3() fmt.Printf(" rethrowLevel2 正常结束\\n") } // rethrowLevel3 重新抛出的第三层 func rethrowLevel3() { defer func() { fmt.Printf(" rethrowLevel3 defer\\n") }() fmt.Printf(" 进入 rethrowLevel3\\n") panic("rethrowLevel3 中的 panic") fmt.Printf(" rethrowLevel3 正常结束\\n") } // handleHTTPRequest 处理HTTP请求 func handleHTTPRequest(path string) { defer func() { if r := recover(); r != nil { fmt.Printf(" HTTP请求 %s 发生 panic,已恢复: %v\\n", path, r) // 在实际应用中,这里会返回 500 错误 } }() fmt.Printf(" 处理请求: %s\\n", path) switch path { case "/api/panic": panic("模拟处理器 panic") case "/api/error": // 这里应该返回错误而不是 panic fmt.Printf(" 请求处理出错\\n") default: fmt.Printf(" 请求处理成功\\n") } } // Job 任务结构 type Job struct { ID int Data string } // processJobSafely 安全处理任务 func processJobSafely(job Job) { defer func() { if r := recover(); r != nil { fmt.Printf(" 任务 %d 处理时发生 panic,已恢复: %v\\n", job.ID, r) } }() fmt.Printf(" 处理任务 %d: %s\\n", job.ID, job.Data) switch job.Data { case "panic任务": panic("任务处理中的 panic") case "error任务": fmt.Printf(" 任务处理出错\\n") default: fmt.Printf(" 任务处理成功\\n") } } // parseIntSafely 安全解析整数 func parseIntSafely(s string) int { defer func() { if r := recover(); r != nil { fmt.Printf(" 解析 '%s' 时发生 panic: %v\\n", s, r) } }() if s == "" { panic("空字符串无法解析") } result, err := strconv.Atoi(s) if err != nil { return 0 // 返回默认值 } return result } // safeFactorial 安全计算阶乘 func safeFactorial(n int) int { defer func() { if r := recover(); r != nil { fmt.Printf(" 计算 %d! 时发生 panic: %v\\n", n, r) } }() return factorial(n) } // factorial 计算阶乘(可能 panic) func factorial(n int) int { if n < 0 { panic("负数没有阶乘") } if n == 0 || n == 1 { return 1 } return n * factorial(n-1) } // callLibraryFunction 库函数(不好的做法) func callLibraryFunction(input string) (string, error) { // 这个函数直接 panic,不是好的做法 if input == "invalid" { panic("库函数中的 panic") } return "处理结果: " + input, nil } // safeLibraryFunction 安全的库函数 func safeLibraryFunction(input string) (result string, err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("库函数内部错误: %v", r) } }() // 调用可能 panic 的函数 return callLibraryFunction(input) } // logPanic 记录 panic 信息 func logPanic(r interface{}) { fmt.Printf(" [PANIC LOG] 时间: %s\\n", time.Now().Format("2006-01-02 15:04:05")) fmt.Printf(" [PANIC LOG] 错误: %v\\n", r) fmt.Printf(" [PANIC LOG] 类型: %T\\n", r) // 获取调用栈 buf := make([]byte, 1024) n := runtime.Stack(buf, false) fmt.Printf(" [PANIC LOG] 调用栈:\\n%s\\n", buf[:n]) } // Server 服务器结构 type Server struct { name string } func (s *Server) Start() { fmt.Printf(" 服务器 %s 启动\\n", s.name) } func (s *Server) Stop() { fmt.Printf(" 服务器 %s 停止\\n", s.name) } func (s *Server) handlePanic() { if r := recover(); r != nil { fmt.Printf(" 服务器 %s 捕获 panic: %v\\n", s.name, r) fmt.Printf(" 服务器继续运行\\n") } } // divide 除法运算 func divide(a, b float64) (float64, error) { if b == 0 { return 0, fmt.Errorf("除数不能为零") } return a / b, nil } // assertPositive 断言为正数 func assertPositive(n int) { if n <= 0 { panic(fmt.Sprintf("断言失败: %d 不是正数", n)) } fmt.Printf(" 断言通过: %d 是正数\\n", n) } // Calculator 计算器 type Calculator struct{} func (c *Calculator) Calculate(expression string) (result float64, err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("计算表达式 '%s' 时发生内部错误: %v", expression, r) } }() switch expression { case "10 + 5": return 15, nil case "invalid": return 0, fmt.Errorf("无效的表达式: %s", expression) case "panic": panic("计算器内部 panic") default: return 0, fmt.Errorf("不支持的表达式: %s", expression) } } /* 运行这个程序: go run 03-panic-recover.go 学习要点: 1. panic 是运行时错误,会导致程序崩溃 2. recover 只能在 defer 函数中使用来捕获 panic 3. defer 语句按照 LIFO 顺序执行 4. panic 会沿着调用栈向上传播 5. panic/recover 应该谨慎使用,优先使用错误处理 Panic 的特性: 1. 停止当前函数的正常执行 2. 执行当前函数的所有 defer 语句 3. 向上传播到调用函数 4. 如果没有被 recover,程序会崩溃 5. 可以携带任意类型的值 Recover 的特性: 1. 只能在 defer 函数中直接调用 2. 返回 panic 的值,如果没有 panic 返回 nil 3. 只能捕获当前 goroutine 的 panic 4. 捕获后程序可以继续执行 5. 可以根据 panic 值进行不同处理 常见的 Panic 场景: 1. 数组或切片越界访问 2. 空指针解引用 3. 类型断言失败(不安全断言) 4. 向已关闭的 channel 发送数据 5. 除零操作(整数除法) 使用场景: 1. Panic 适用场景: - 程序逻辑错误 - 不可恢复的错误 - 初始化失败 - 断言失败 2. Error 适用场景: - 预期可能发生的错误 - 用户输入错误 - 网络或I/O错误 - 业务逻辑错误 最佳实践: 1. 优先使用错误返回值而不是 panic 2. 只在真正异常的情况下使用 panic 3. 在库代码中避免 panic,转换为错误 4. 在应用边界使用 recover 保护 5. 记录 panic 信息用于调试 6. 使用 defer 进行资源清理 注意事项: 1. recover 只能在 defer 中直接调用 2. 不要忽略 recover 的返回值 3. panic 会影响程序性能 4. 过度使用 panic/recover 会使代码难以理解 5. 在 goroutine 中的 panic 需要单独处理 实际应用: 1. Web 服务器的 panic 恢复中间件 2. 工作池中的任务 panic 处理 3. 数据解析中的异常处理 4. 递归函数的栈溢出保护 5. 库函数的 panic 转错误 性能考虑: 1. panic/recover 有一定的性能开销 2. 不应该用于正常的控制流 3. 频繁的 panic/recover 会影响性能 4. 错误处理通常比 panic/recover 更高效 5. 在性能敏感的代码中谨慎使用 */