Files
golang/golang-learning/07-error-handling/03-panic-recover.go
2025-08-24 11:24:52 +08:00

942 lines
21 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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. 在性能敏感的代码中谨慎使用
*/