flyfei

错误处理

1. 语法讲解

Go语言的错误处理机制:

// 恢复panic func recover() interface{}


### 2. 应用场景

- **error类型**:数据库连接失败、文件读写错误
- **defer语句**:资源清理(关闭文件、释放锁、数据库连接)
- **panic/recover**:处理不可恢复的错误(如数组越界)

### 3. 编程实例

```go
package main

import (
    "errors"
    "fmt"
    "os"
    "time"
)

// 自定义错误类型
type BusinessError struct {
    Code    int
    Message string
    Time    time.Time
}

func (e *BusinessError) Error() string {
    return fmt.Sprintf("错误代码: %d, 消息: %s, 时间: %s", 
        e.Code, e.Message, e.Time.Format("2006-01-02 15:04:05"))
}

// 数据库操作模拟
func queryDatabase(userId int) (string, error) {
    if userId <= 0 {
        return "", &BusinessError{
            Code:    400,
            Message: "无效的用户ID",
            Time:    time.Now(),
        }
    }
    
    // 模拟数据库连接失败
    if userId == 999 {
        return "", errors.New("数据库连接超时")
    }
    
    return "用户数据", nil
}

// 文件操作:使用defer确保资源释放
func readFile(filename string) error {
    file, err := os.Open(filename)
    if err != nil {
        return fmt.Errorf("打开文件失败: %w", err)
    }
    defer file.Close() // 确保文件会被关闭
    
    // 模拟文件读取
    buf := make([]byte, 100)
    _, err = file.Read(buf)
    if err != nil {
        return fmt.Errorf("读取文件失败: %w", err)
    }
    
    return nil
}

// panic/recover示例:处理数组越界
func safeAccess(arr []int, index int) (result int, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("发生panic: %v", r)
        }
    }()
    
    result = arr[index] // 可能触发panic
    return result, nil
}

func main() {
    // 错误处理示例
    fmt.Println("=== 数据库查询测试 ===")
    
    // 正常查询
    data, err := queryDatabase(1)
    if err != nil {
        fmt.Println("查询失败:", err)
    } else {
        fmt.Println("查询结果:", data)
    }
    
    // 无效ID查询
    _, err = queryDatabase(-1)
    if err != nil {
        fmt.Println("查询失败:", err)
    }
    
    // 数据库连接失败
    _, err = queryDatabase(999)
    if err != nil {
        fmt.Println("查询失败:", err)
    }
    
    // defer使用示例
    fmt.Println("\n=== defer使用示例 ===")
    func() {
        defer fmt.Println("1. 第一个defer")
        defer fmt.Println("2. 第二个defer")
        fmt.Println("3. 函数体")
    }()
    
    // panic/recover示例
    fmt.Println("\n=== panic/recover测试 ===")
    arr := []int{1, 2, 3}
    
    // 正常访问
    result, err := safeAccess(arr, 1)
    if err != nil {
        fmt.Println("访问失败:", err)
    } else {
        fmt.Println("访问成功:", result)
    }
    
    // 越界访问
    result, err = safeAccess(arr, 10)
    if err != nil {
        fmt.Println("访问失败:", err)
    } else {
        fmt.Println("访问成功:", result)
    }
    
    fmt.Println("程序继续执行...")
}

4. 其他用法

package main

import (
    "errors"
    "fmt"
    "os"
)

func readConfig() error {
    err := readFile("config.txt")
    if err != nil {
        return fmt.Errorf("读取配置文件失败: %w", err)
    }
    return nil
}

func readFile(filename string) error {
    _, err := os.Stat(filename)
    if err != nil {
        return fmt.Errorf("检查文件状态失败: %w", err)
    }
    return nil
}

func main() {
    err := readConfig()
    if err != nil {
        fmt.Println("发生错误:", err)
        
        // 解包错误链
        var pathError *os.PathError
        if errors.As(err, &pathError) {
            fmt.Printf("路径错误: %s\n", pathError.Path)
        }
    }
}
package main

import "fmt"

// defer修改返回值
func deferReturn() (result int) {
    defer func() {
        result++ // 修改命名返回值
    }()
    return 10 // 实际返回11
}

// defer与recover结合
func safeDivide(a, b int) (result int, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("除法出错: %v", r)
        }
    }()
    
    result = a / b
    return result, nil
}

func main() {
    // defer修改返回值
    fmt.Println("deferReturn:", deferReturn()) // 输出11
    
    // 安全的除法
    result, err := safeDivide(10, 0)
    if err != nil {
        fmt.Println("错误:", err)
    } else {
        fmt.Println("结果:", result)
    }
}

5. 课时总结

本课时我们学习了: