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的每个包中增加一个错误对象定义文件,用来保存定义的错误。