diff --git a/day03/error/README.md b/day03/error/README.md index 73308d4..7c0d0d4 100644 --- a/day03/error/README.md +++ b/day03/error/README.md @@ -1 +1,159 @@ -# 异常处理 \ No newline at end of file +# 异常处理 + +程序运行过程中,可能会有异常,比如: 输入参数错误,文件不存在,数据库连接失败等等 + +## error (程序异常) + +### 处理 error + +异常处理的流派: ++ 通过返回值值处理: resp, error := func() (resp, error) ++ try catch: try { ... } catch(e) { ... }, 面向对象的语言: java, python ,js, + +通过一个返回值来处理异常 + +```go +resp, err := func() (resp, error) +if err != nil { + fmt.Println("Error:", err) + return nil +} +``` + +```go +func main() { + // 变量目录, 打印了文件名称 + err := walkDir("./xxxx", func(filePath string) { + fmt.Println(filePath) + }) + if err != nil { + fmt.Println("Error:", err) + } +} + +// 最常见的就是 遍历目录里面的文件 +func walkDir(path string, fn func(string)) error { + files, err := os.ReadDir(path) + if err != nil { + return err + } + + // 读取path目录下的文件和子目录 + for _, file := range files { + // 如果是目录, 继续往下找 + if file.IsDir() { + walkDir(path+"/"+file.Name(), fn) + } else { + // 文件, 调用fn函数处理 + fn(path + "/" + file.Name()) + } + } + + return nil +} +``` + +### 怎么抛出error + +1. 直接返回 底层的error到上层 (return error) +```go +return err +``` + +2. 通过error包来New一个error (errors.New) +```go +files, err := os.ReadDir(path) +if err != nil { + return errors.New(fmt.Sprintf("File Read Error: %s", err)) +} +``` + +2. 直接封装下,封装成自己的error(fmt.Errorf) +```go +files, err := os.ReadDir(path) +if err != nil { + return fmt.Errorf("File Read Error: %s", err) +} +``` + +3. 组合异常,再底层的异常上,添加自己定义的异常(errors.Join) +```go +files, err := os.ReadDir(path) +if err != nil { + return errors.Join(errors.New("walkDir error"), err) +} +``` + +### 异常的比较 (使用 errors.Is) + +```go +var ( + ErrInvalid = errors.New("invalid argument") + ErrPermission = errors.New("permission denied") + ErrExist = errors.New("file already exists") + ErrNotExist = errors.New("file does not exist") + ErrClosed = errors.New("file already closed") +) +``` + +```go +if err != nil { + if errors.Is(err, os.ErrNotExist) { + fmt.Println(ErrFileNotFound) + } else { + fmt.Printf("%s", err) + } + os.Exit(1) +} +``` + +## panic (程序崩溃) + +### pannic + +非常危险,空指针(NPE): + +windows蓝屏这种, 特别危险的操作, 如果继续运行可能会造成安全隐患, 内存的非法访问, A程序的内存(内存地址A, 用完后把内存地址释放, OS 这一个物理内存 分配给了程序B, A再次访问已经释放的内存地址A), 崩溃的其中一个场景,内存非发访问 + +go 是可以 绕开指针类型系统(nil), 直接使用unsafe包,访问地址地址 + +```go +// pannic +// 索引4 就是非法内存 +arrs := []int{1, 2, 3} +fmt.Println(arrs[4]) +``` + +```sh +panic: runtime error: index out of range [4] with length 3 + +goroutine 1 [running]: +main.main() + /Users/yumaojun/Projects/go-course/go20/day03/error/main.go:28 +0x44 +exit status 2 +``` + +### recover: 从崩溃中恢复 + +你写一个API Server, 不小心 panic: runtime error: index out of range [4] with length 3, 程序崩溃,无法继续处理用户请求,你网站有1000个接口,产生这个报错的是其中一个接口的一个流程,需要阻止程序崩溃,打印日志就行 + +捕获panic信号, 捕获是一个特殊逻辑,程序退出前 去检查有没有panic信号的, 放执行前面和后面都不行,需要放到 函数调用结束后执行(Hook) +```go +func recoverHandler() { + if r := recover(); r != nil { + fmt.Println("Recovered from panic:", r) + os.Exit(1) + } +} + +func main() { + defer recoverHandler() + ... +} +``` + +## defer (函数Hook) + +函数执行完成后,调用你的defer 函数进行 资源清理或者崩溃恢复 + + diff --git a/day03/error/main.go b/day03/error/main.go index 7905807..4caa3f7 100644 --- a/day03/error/main.go +++ b/day03/error/main.go @@ -1,5 +1,69 @@ package main -func main() { +import ( + "errors" + "fmt" + "os" +) +var ErrFileNotFound = errors.New("file not found") + +func recoverHandler() { + if r := recover(); r != nil { + fmt.Println("Recovered from panic:", r) + os.Exit(1) + } +} + +func main() { + defer recoverHandler() + + // if r := recover(); r != nil { + // fmt.Println("Recovered from panic:", r) + // os.Exit(1) + // } + + // 变量目录, 打印了文件名称 + err := walkDir("./xxxx", func(filePath string) { + fmt.Println(filePath) + }) + + if err != nil { + if errors.Is(err, os.ErrNotExist) { + fmt.Println("not exist", err) + } else { + fmt.Printf("%s", err) + } + // os.Exit(1) + } + + // pannic + arrs := []int{1, 2, 3} + fmt.Println(arrs[4]) + + // if r := recover(); r != nil { + // fmt.Println("Recovered from panic:", r) + // os.Exit(1) + // } +} + +// 最常见的就是 遍历目录里面的文件 +func walkDir(path string, fn func(string)) error { + files, err := os.ReadDir(path) + if err != nil { + return errors.Join(errors.New("walkDir error"), err) + } + + // 读取path目录下的文件和子目录 + for _, file := range files { + // 如果是目录, 继续往下找 + if file.IsDir() { + walkDir(path+"/"+file.Name(), fn) + } else { + // 文件, 调用fn函数处理 + fn(path + "/" + file.Name()) + } + } + + return nil } diff --git a/day03/error/mpg.drawio b/day03/error/mpg.drawio new file mode 100644 index 0000000..7ef5927 --- /dev/null +++ b/day03/error/mpg.drawio @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file