概述
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() }