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