01简介在Golang语言中,触发panic的程序会导致程序崩溃,所以我们在开发程序的时候需要非常小心,避免触发panic。在这篇文章中,我们介绍了Golang语言中比较容易引起panic的操作。02Pointer任何编程语言都会用到函数。我们在使用Golang写函数或者方法的时候,经常会用到指针类型的返回值。这时候如果执行调用了一个空指针(指针未初始化或者值为nil),对于新手来说很容易造成程序死机。typeUserstruct{NamestringAgeint}func(u*User)GetInfo()(data*User){data=&User{Name:"frank",Age:18,}returndata}funcmain(){user:=new(User)userInfo:=user.GetInfo()fmt.Println(userInfo)ifuserInfo.Age>=18{fmt.Println("thisisaman")}}我们看了上面的代码,这是一个很简单的返回指针类型的示例代码,想想看,读者。如果GetInfo方法体中data的值来自调用另一个函数或方法,而被调用函数或方法的返回值为nil,而我们的main函数会以返回值的Age字段作为判断条件,那么程序会触发panic,导致程序崩溃。所以我们在使用指针类型的时候一定要非常小心,否则只能使用defer和recover在调用函数或者方法前加一段补偿代码,个人感觉不是很优雅。deferfunc(){iferr:=recover();err!=nil{fmt.Println("err=",err)}}()我一般判断返回值是指针类型,以避免程序造成panic,我会加A和(&&)判断条件,判断返回值不为nil,返回值的某个字段满足某个条件。funcmain(){...ifuserInfo!=nil&&userInfo.Age>=18{fmt.Println("thisisaman")}}03数组和切片数组和切片类型,当我们越界访问的时候,也会触发panic,导致程序崩溃。但是一般的IDE会提示数组越界访问的错误。如果读者使用的编辑器没有提示数组越界的错误,那你在使用数组的时候就要小心了。funcmain(){code:=[]string{"php","golang"}fmt.Printf("len:%dcap:%dval:%s\n",len(code),cap(code),code)fmt.Println(code[2])}04channel如果我们关闭未初始化的通道,重复关闭通道,向关闭的通道发送数据,这三种情况也会造成panic,导致程序崩溃。funcmain(){varchchanint//close(ch)ch=make(chanint,1)ch<-1//close(ch)//close(ch)ch<-2}05映射如果我们直接操作未初始化的映射(map),也会引发恐慌,导致程序崩溃。funcmain(){varmmap[string]int//m=make(map[string]int)m["php"]=80}另外,操作map可能遇到的一个比较严重的问题是,同时,同一个map并发读写,会触发runtime.throw,不像panic,可以通过recover捕获。因此,当我们对同一个map并发读写时,就必须使用锁。funcmain(){varmmap[string]intm=make(map[string]int)gofunc(map[string]int){for{m["php"]=80}}(m)gofunc(map[string]int){for{_=m["php"]}}(m)time.Sleep(time.Second)}输出结果:fatalerror:concurrentmapreadandmapwritegoroutine7[running]:runtime.throw({0x10a7510,0x0})/usr/local/opt/go/libexec/src/runtime/panic.go:1198+0x71fp=0xc000050f28sp=0xc000050ef8pc=0x102fa5106类型断言如果类型断言使用不当,比如我们不接受布尔值,则类型断言失败也会造成恐慌,导致程序崩溃。funcmain(){varnameinterface{}="frank"//a,ok:=name.(int)//fmt.Println(a,ok)a:=name.(int)fmt.Println(a)}07总结在这篇文章中,我们介绍了Golang语言中容易出现panic的场景,尤其是空指针操作是最容易踩到的场景。我们在程序开发中使用指针类型时一定要小心。
