type关键字
go语言中type关键字主要是用来定义结构体以及接口类型的,但它也有别的作用,如用来起别名、定义其他的新类型等
1.定义struct
type name struct{ // }
2.定义interface
type name interface{ // }
3.定义其他的新类型
type name Type
type myint int type mtstr string func main(){ var i1 int = 100 var i2 myint = 200 var s1 string = "hello" var s2 mystr = "world" fmt.Printf("%T,%T,%T,%T\n",i1,i2,s1,s2) //var i3 myint = i1 //==>报错,这是两种类型了不能共通 }
//定义一种函数类型 type myfunc func(int,int)(int) func test() myfunc{//test()的返回值是myfunc类型 fun := func(a,b int)int{ return a+b } return fun } func main(){ result := test() fmt.Println(result(100,200)) }
4.类型别名
type 别名 = Type
type myint = int//此时myint和int是同一种数据类型
错误和异常是撒子?
错误是指可能出现问题的地方出现了问题,是在人们的意料之中的
异常是指在不该出现问题的地方出问题了,在人们的意料之外
错误是业务过程的一部分,而异常不是
go语言中的错误也是一种类型,用内置的error类型表示。错误值可以存储在变量中,从函数返回。
error的使用
1.判断错误的方法
我们使用的函数若是有返回错误信息的,错误通常是作为最后一个返回值返回。我们只要检查它是否为nil就能知道是否发生错误,为nil即正常运行,非nil则是发生了错误。
2.创建新的错误对象
error实质上是一个一个接口,它定义了一个Error方法
//1.使用errors包下的New方法可以创建一个error err1 := errors.New("自己创的错误类型一") fmt.Println(err1) fmt.Printf("%T\n",err1) //2.使用fmt包下的Errorf函数可以格式化的创建error errnum := 404 err2 := fmt.Errorf("自创的错误码:%d",errnum) fmt.Println(err2) fmt.Printf("%T\n",err2) //以上两种方式%T得到的类型均为*errors.errorStrting
//设计一个函数验证年龄 func isAgeLegal(age int) error { if age > 18 && age < 60 { fmt.Println("巴适") return nil } else { return fmt.Errorf("年龄不合适") } } func main() { err := isAgeLegal(10) if err != nil { fmt.Println(err) } }
3.使用断言将error转化为具体实现,再去调用实现的Error方法,可以输出具体的error信息
4.常见的错误可以直接通过是否全等来进行判断
自定义错误
步骤:
1.定义一个结构体,表示错误的类型
2.实现error接口(即实现Error方法)
3.有某个函数或方法的返回值是该自定义的错误
4.调用函数
//实例:定义一个三角形三边不合法会报的错误 type myError struct { a, b, c float64 msg string } func (err *myError) Error() string { return fmt.Sprintf("%s,三条边分别为%.2f,%.2f,%.2f", err.msg, err.a, err.b, err.c) } //函数:求三角形的周长 func getC(a, b, c float64) (float64, error) { if a+b > c && a+c > b && b+c > a { return a + b + c, nil } else { return 0, &myError{a, b, c, "无法构成三角形"} } } func main() { c, err := getC(8, 3.5, 15) if err != nil { if ins, ok := err.(*myError); ok { fmt.Println("三边长分别为:", ins.a, ins.b, ins.c) } fmt.Println(err.Error()) } else { fmt.Println("周长为:", c) } }
panic()和recover()
go语言不支持传统的try-catch来抛出异常,在面对异常时使用panic()以及()recover()来进行处理
1.panic():让当前的程序进入恐慌,中断程序的执行
-
当外围函数的代码发生了运行恐慌,只有其中所有已经defer的函数全部执行完毕后,该运行时恐慌才会真正的传到被调用处。
-
在panic前defer的程序才会被成功defer,panic后的程序不再执行(拓展至被调用处后面的程序也不执行了)
func myprint(s string) { //定义一个函数用来defer时输出信息 fmt.Println(s) } func fun1() { defer myprint("fun1...1") for i := 0; i < 10; i++ { fmt.Println("i:", i+1) if i == 5 { panic("恐慌啦!!!") } } defer myprint("fun1...2") } func main() { defer myprint("main...1") fun1() defer myprint("main...2") }
//程序运行结果: i: 1 i: 2 i: 3 i: 4 i: 5 i: 6 fun1...1 main...1 panic: 恐慌啦!!! goroutine 1 [running]: main.fun1() F:/vscodePro/goPro/src/01test/01test.go:16 +0x19c main.main() F:/vscodePro/goPro/src/01test/01test.go:25 +0x67 exit status 2
2.recover():捕获panic,恢复被恐慌的程序
通常使用defer先定义一个函数,函数中使用recover来捕获程序中出现的恐慌,recover执行之后程序就会恢复正常
//在上面的程序中加入recover func myprint(s string) { fmt.Println(s) } func fun1() { defer myprint("fun1...1") for i := 0; i < 10; i++ { fmt.Println("i:", i+1) if i == 5 { panic("恐慌啦!!!") } } defer myprint("fun1...2") } func main() { defer func() { msg := recover() if msg != nil { fmt.Println(msg,"恐慌被解除啦!!!") } }() defer myprint("main...1") fun1() defer myprint("main...2") }
//执行结果: i: 1 i: 2 i: 3 i: 4 i: 5 i: 6 fun1...1 main...1 恐慌啦!!!恐慌被解除啦!!!
3.关于panic()的参数以及recover()的返回值:recover函数的返回值就是它所捕获的panic函数调用时所传递的参数
使用错误和异常的时机
1. 啥时候使用异常?
-
空指针引用
-
下标越界
-
除数为0
-
不应该出现的分支
-
输入不应该引起函数错误
上述这样子会导致程序崩坏的严重性错误就应该使用异常,再在恰当的时机进行恢复
2.啥时候使用错误?
除了上述情况使用异常之外其他情况使用错误进行处理即可。
但也不是什么错误都需要通过error来返回,当错误只有一种情况时,直接使用bool就能判断出来,就不需要使用error了
在使用错误的使用,为了避免定义了很多个意义相近或相同的错误(影响重构),我们可以在golang的每个包中增加一个错误对象定义文件,用来保存定义的错误。