概述
go中发生panic的场景: - 数组/切片越界 - 空指针调用。比如访问一个 nil 结构体指针的成员 - 过早关闭 HTTP 响应体 - 除以 0 - 向已经关闭的 channel 发送消息 - 重复关闭 channel - 关闭未初始化的 channel - 未初始化 map。注意访问 map 不存在的 key 不会 panic,而是返回 map 类型对应的零值,但是不能直接赋值 - 跨协程的 panic 处理 - sync 计数为负数。 - 类型断言不匹配。`var a interface{} = 1; fmt.Println(a.(string))` 会 panic,建议用 `s,ok := a.(string)`
代码
package a_panics import ( "fmt" "math/rand" "testing" "time" ) type Student struct { Name string Age int } func TestP0(t *testing.T) { // 空指针调用: 访问一个 nil 结构体指针的成员 var s *Student fmt.Println(s.Name) } func TestP1(t *testing.T) { // 除数为0 fmt.Println(100 / 0) // rand.Intn方法:如果里面的参数 <=0 的话,会panic rand.Seed(time.Now().UnixNano()) fmt.Println(rand.Intn(0)) // 数组/切片越界 lst := []string{"whw", "naruto", "sasuke"} fmt.Println(lst[0:22]) // 为初始化map var m1 map[string]interface{} fmt.Println(m1["name"]) // 直接取值不会panic m1["age"] = 22 /* // 下面这样可以 m1 := make(map[string]interface{}, 0) m1["age"] = 22 */ // 断言类型不匹配 var a interface{} = 1 fmt.Println(a.(string)) /* // 建议这样写: s,ok := a.(string) fmt.Println("ok: ", ok, "s: ", s) */ }
~~~
package a_panics import ( "fmt" "io/ioutil" "net/http" "testing" ) func TestHttpBody(t *testing.T) { url := "https://www.baidu.com" method := "GET" client := &http.Client{} req, err := http.NewRequest(method, url, nil) if err != nil { fmt.Println(err) return } res, err := client.Do(req) if err != nil { fmt.Println(err) return } // TODO (不加defer)过早关闭HTTP响应体 defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { fmt.Println(err) return } fmt.Println(string(body)) }
~~~
package a_panics import "fmt" import "testing" func TestChannel0(t *testing.T) { //ch := make(chan int) // 关闭未初始化的channel var ch chan int close(ch) } func TestChannel1(t *testing.T) { ch := make(chan int) // no-cached channel go func() { // 子goroutine中向channel中存放值 ch <- 1 close(ch) }() // 主 goroutine 接收channel的值 x := <-ch fmt.Println("x: ", x) // TODO // 像已关闭的channel发送消息 ch <- 2 } func TestChannel2(t *testing.T) { ch := make(chan int) // no-cached channel go func() { // 子goroutine中向channel中存放值 ch <- 1 close(ch) }() // 主 goroutine 接收channel的值 x := <-ch fmt.Println("x: ", x) // TODO 重复关闭channel close(ch) }
~~~
package a_panics import ( "fmt" "testing" ) func test(){ defer func(){ // 捕获异常 if err := recover(); err != nil{ fmt.Println("errFromFunc:test", err) } }() var m1 map[string]interface{} m1["age"] = 22 } func sayHello(){ fmt.Println("hello!") } func TestP4(t *testing.T){ go test() go sayHello() }