1、系统自己抛异常
//go语言抛异常 func test3_1() { l := [5] int {0,1,2,3,4} var index int = 6 fmt.Println(l) l[index] = 6 }
测试结果
//test3_1() //panic这个关键字,这是系统自己抛的异常 //panic: runtime error: index out of range
2、用户抛异常,使用panic关键字
func test3_2(s float64) (m float64) { if s < 0 { panic("该参数为半径,不能小于0") }else { return 3.14 * s * s } }
测试结果
//正常的参数,不会抛异常 //res := test3_2(3.6) //fmt.Println(res) //40.6944 //res := test3_2(-3.6) //fmt.Println(res) //panic: 该参数为半径,不能小于0
3、使用defer+recover捕获函数执行过程中的异常并打印
func test3_3() { defer func() { //这个recover会捕获函数执行的异常的错误信息,我们这里的例子是执行test3_2这个函数报错,使用panic抛出了异常,这里recover就会接受到这个异常,然后打印出来 err := recover() if err != nil { fmt.Println(err,"1111") } }() test3_2(-2.34) fmt.Println("这里是函数3") }
测试结果
//test3_3() //该参数为半径,不能小于0 1111
我们看到“这里是函数3”这句话没有打印,因为上面的函数执行出错了,就直接执行defer语句了
4、defer+recover用法2
func test3_3() { defer func() { //这个recover会捕获函数执行的异常的错误信息,我们这里的例子是执行test3_2这个函数报错,使用panic抛出了异常,这里recover就会接受到这个异常,然后打印出来 err := recover() if err != nil { fmt.Println(err,"1111") } }() test3_2(-2.34) fmt.Println("这里是函数3") } func test3_4() { test3_3() fmt.Println("这里是函数4") }
测试结果
//test3_4() //该参数为半径,不能小于0 1111 //这里是函数4
这里打印了“这里是函数4”,主要是因为test3_3这个函数执行本身没有报错
5、使用errors.NEW返回异常,并捕获打印
//接受异常 func test3_5(b float64) (m float64,e error) { if b < 0 { e = errors.New("傻屌,半径不能为负数") return }else { m = b * b *3.14 return } }
测试结果
m,e := test3_5(3.21) fmt.Println(m,e) //32.354874 <nil> m1,e1 := test3_5(-3.21) fmt.Println(m1,e1) //0 傻屌,半径不能为负数
Go 错误处理
Go 语言通过内置的错误接口提供了非常简单的错误处理机制。
error类型是一个接口类型,这是它的定义:
type error interface { Error() string }
我们可以在编码中通过实现 error 接口类型来生成错误信息。
函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息:
func Sqrt(f float64) (float64, error) { if f < 0 { return 0, errors.New("math: square root of negative number") } // 实现 }
在下面的例子中,我们在调用Sqrt的时候传递的一个负数,然后就得到了non-nil的error对象,将此对象与nil比较,结果为true,所以fmt.Println(fmt包在处理error时会调用Error方法)被调用,以输出错误,请看下面调用的示例代码:
result, err:= Sqrt(-1) if err != nil { fmt.Println(err) }
package main import ( "fmt" ) // 定义一个 DivideError 结构 type DivideError struct { dividee int divider int } // 实现 `error` 接口 func (de *DivideError) Error() string { strFormat := ` Cannot proceed, the divider is zero. dividee: %d divider: 0 ` return fmt.Sprintf(strFormat, de.dividee) } // 定义 `int` 类型除法运算的函数 func Divide(varDividee int, varDivider int) (result int, errorMsg string) { if varDivider == 0 { dData := DivideError{ dividee: varDividee, divider: varDivider, } errorMsg = dData.Error() return } else { return varDividee / varDivider, "" } } func main() { // 正常情况 if result, errorMsg := Divide(100, 10); errorMsg == "" { fmt.Println("100/10 = ", result) } // 当被除数为零的时候会返回错误信息 if _, errorMsg := Divide(100, 0); errorMsg != "" { fmt.Println("errorMsg is: ", errorMsg) } }
执行以上程序,输出结果为:
100/10 = 10 errorMsg is: Cannot proceed, the divider is zero. dividee: 100 divider: 0
这里应该介绍一下 panic 与 recover,一个用于主动抛出错误,一个用于捕获panic抛出的错误。
概念
panic 与 recover 是 Go 的两个内置函数,这两个内置函数用于处理 Go 运行时的错误,panic 用于主动抛出错误,recover 用来捕获 panic 抛出的错误。
- 引发
panic
有两种情况,一是程序主动调用,二是程序产生运行时错误,由运行时检测并退出。 - 发生
panic
后,程序会从调用panic
的函数位置或发生panic
的地方立即返回,逐层向上执行函数的defer
语句,然后逐层打印函数调用堆栈,直到被recover
捕获或运行到最外层函数。 panic
不但可以在函数正常流程中抛出,在defer
逻辑里也可以再次调用panic
或抛出panic
。defer
里面的panic
能够被后续执行的defer
捕获。recover
用来捕获panic
,阻止panic
继续向上传递。recover()
和defer
一起使用,但是defer
只有在后面的函数体内直接被掉用才能捕获panic
来终止异常,否则返回nil
,异常继续向外传递。
例子1
//以下捕获失败 defer recover() defer fmt.Prinntln(recover) defer func(){ func(){ recover() //无效,嵌套两层 }() }() //以下捕获有效 defer func(){ recover() }() func except(){ recover() } func test(){ defer except() panic("runtime error") }
例子2
多个panic只会捕捉最后一个:
package main import "fmt" func main(){ defer func(){ if err := recover() ; err != nil { fmt.Println(err) } }() defer func(){ panic("three") }() defer func(){ panic("two") }() panic("one") }
使用场景
一般情况下有两种情况用到:
- 程序遇到无法执行下去的错误时,抛出错误,主动结束运行。
- 在调试程序时,通过 panic 来打印堆栈,方便定位错误。
在看一个事例
if result, errorMsg := Divide(100, 10); errorMsg == "" { fmt.Println("100/10 = ", result) } if _, errorMsg := Divide(100, 0); errorMsg != "" { fmt.Println("errorMsg is: ", errorMsg) }
上面的事例等价于下面的事例
result, errorMsg := Divide(100,10) if errorMsg == ""{ fmt.Println("100/10 = ", result) } result, errorMsg = Divide(100,0) if errorMsg != ""{ fmt.Println("errorMsg is: ", errorMsg) }
fmt.Println 打印结构体的时候,会把其中的 error 的返回的信息打印出来
type User struct { username string password string } func (p *User) init(username string ,password string) (*User,string) { if ""==username || ""==password { return p,p.Error() } p.username = username p.password = password return p,""} func (p *User) Error() string { return "Usernam or password shouldn't be empty!"} } func main() { var user User user1, _ :=user.init("",""); fmt.Println(user1) }
结果如下
Usernam or password shouldn't be empty!
字符串操作
package main import ( "fmt" "strconv" ) //字符串转换 //Append系列函数,把整数等转换为字符串,添加到现有的字节数组中 //Format系列函数,把其他类型的转换为字符串 //Parse系列函数,把字符串转换为其他类型 func main() { str := make([]byte,0,100) //10是十进制的意思 str = strconv.AppendInt(str,456,16) str = strconv.AppendBool(str,false) str = strconv.AppendQuote(str,"abc") fmt.Println(string(str)) }