Go语言学习教程(十二)

一、断言

* 只要实现了接口的全部方法就认为这个类型属于接口类型,如果编写一个接口,这个接口中没有任何方法,这时认为所有类型都实现了这个接口.所以Go语言中interface{}代表任意类型

* 如果interface{}作为方法参数就可以接收任意类型,但是在程序中有时需要知道这个参数到底是什么类型,这个时候就需要使用断言

* 断言使用时,使用interface{}变量点括号,括号中判断是否属于的类型

    i interface{}

    i.(Type)

* 断言的两大作用:

    * 判断是否是指定类型

    * 把interface{}转换为特定类型

* 断言可以有一个返回值,如果判断结果是指定类型返回变量值,如果不是指定类型则报错

    func demo(i interface{}){

        result:=i.(int)

        fmt.Println(result)

    }

    func main() {

        /*

        参数是456时,程序运行正常,输出456

        参数是false时报错:

        panic: interface conversion: interface {} is bool, not int

         */

        demo(456)

    }

* 断言也可以有两个返回值,这时无论是否是指定类型都不报错.

    * 第一个参数:

        * 如果正确:返回值变量值

        * 如果错误:返回判断类型的默认值

    * 第二个参数:

        * 返回值为bool类型,true表示正确,false表示错误

    func demo(i interface{}) {

        result, ok := i.(int)

        fmt.Println(result, ok)

    }

    func main() {

        /*

        参数是456时,程序运行正常,输出456    true

        参数是字符串"abc"时程序运行正常,输出0 false

     */

        demo("abc")

    }

二、错误

* 在程序执行过程中出现的不正常情况称为错误

* Go语言中使用builtin包下的error接口作为错误类型,官方源码定义如下

    * 只包含了一个方法,方法返回值是string,表示错误信息

    type error interface {

        Error() string

    }

* Go语言中错误都作为方法/函数的返回值,因为Go语言认为使用其他语言类似try...catch这种方式会影响到程序结构

* 在Go语言标准库的errors包中提供了error接口的实现结构体errorString,并重写了error接口的Error()方法.额外还提供了快速创建错误的函数

* 如果错误信息由很多变量(小块)组成,可以借助fmt.Errorf("verb",...)完成错误信息格式化,因为底层还是errors.New()

三、自定义错误

* 使用Go语言标准库创建错误,并返回

    func demo(i, k int) (d int, e error) {

        if k == 0 {

            e = errors.New("初始不能为0")

            d=0

            return

        }

        d = i / k

        return

    }

    func main() {

        result,error:=demo(6,0)

        fmt.Println(result,error)

    }

* 如果错误信息由多个内容组成,可以使用下面实现方式

    func demo(i, k int) (d int, e error) {

        if k == 0 {

            e = fmt.Errorf("%s%d和%d", "除数不能是0,两个参数分别是:", i, k)

            d = 0

            return

        }

        d = i / k

        return

    }

    func main() {

        result, error := demo(6, 0)

        fmt.Println(result, error)

    }

四、Go语言中错误处理方式

* 可以忽略错误信息,使用占位符

    result, _ := demo(6, 0)

* 使用if处理错误,原则上每个错误都应该解决

func main() {

    result, error := demo(6, 0)

    if error != nil {

        fmt.Println("发生错误", error)

        return

    }

    fmt.Println("程序执行成功,结果为:", result)

}

五、defer使用

* Go语言中defer可以完成延迟功能,当前函数执行完成后执行defer功能

* defer最常用的就是关闭连接(数据库连接,文件等)可以打开连接后代码紧跟defer进行关闭,后面在执行其他功能

    * 在很多语言中要求必须按照顺序执行,也就是必须把关闭代码写在最后,但是经常会忘记关闭导致内存溢出,而Golang中的defer很好的解决了这个问题.无论defer写到哪里都是最后执行

func main() {

   fmt.Println("打开连接")

   defer func(){

      fmt.Println("关闭连接")

   }()

   fmt.Println("进行操作")

   //输出:打开连接 进行操作 关闭连接

}

* 多重defer采用栈结构执行,先产生后执行

* 在很多代码结构中都可能出现产生多个对象,而程序希望这些对象倒序关闭,多个defer正好可以解决这个问题

func main() {

   fmt.Println("打开连接A")

   defer func(){

      fmt.Println("关闭连接A")

   }()

   fmt.Println("打开连接B")

   defer func(){

      fmt.Println("关闭连接B")

   }()

   fmt.Println("进行操作")

   //输出:打开连接A 打开连接B 进行操作 关闭连接B 关闭连接A

}

* defer与return同时存在时,要把return理解成两条执行结合(不是原子指令),一个指令是给返回值赋值,另一个指令返回跳出函数

* defer和return时整体执行顺序

    * 先给返回值赋值

    * 执行defer

    * 返回跳出函数

* 没有定义返回值接收变量,执行defer时返回值已经赋值

func f() int{

    i:=0

    defer func(){

        i=i+2

    }()

    return i

}

func main() {

    fmt.Println(f())//输出:0

}

* 声明接收返回值变量,执行defer时修改了返回值内容.

    * 由于return后面没有内容,就无法给返回值赋值,所以执行defer时返回值才有内容

func f() (i int){

    defer func(){

        i=i+2

    }()

    return

}

func main() {

    fmt.Println(f())//输出:2

}

六、panic

* panic是builtin中函数

* panic有点类似于其他编程语言的throw,抛出异常.当执行到panic后终止剩余代码执行.并打印错误栈信息

* 注意panic不是立即停止程序(os.Exit(0)),defer还是执行的.

七、recover

* recover()表示恢复程序的panic(),让程序正常运行

* recover()是和panic(v)一样都是builtin中函数,可以接收panic的信息,恢复程序的正常运行

* recover()一般用在defer内部,如果没有panic信息返回nil,如果有panic,recover会把panic状态取消

* recover()只能恢复当前函数级或当前函数调用函数中的panic(),恢复后调用当前级别函数结束,但是调用此函数的函数可以继续执行.

* panic会一直向上传递,如果没有recover()则表示终止程序,但是碰见了recover(),recover()所在级别函数表示没有panic,panic就不会向上传递

八、os包结构介绍

* Go语言标准库中os包提供了不依赖平台的操作系统接口

* 设计为Unix风格的,而错误处理是go风格的,失败的调用会返回错误值而非错误码。通常错误值里包含更多信息

* os包及子包功能

    -- os 包

      --os/exec 包,负责执行外部命令.

      --os/signal对输入信息的访问

      --os/user 通过名称或ID    查询用户账户

* 在os/user中提供了User结构体,表示操作系统用户

    * Uid 用户id

    * Gid 所属组id

    * Username 用户名

    * Name 所属组名

    * HomeDir 用户对应文件夹路径

* 在os/user中的Group表示用户所属组

    * Gid 组的id

    * Name 组的名称

* 整个os/user包中内容比较少,提供了两个错误类型和获取当前用户,查找用户

* 可以获取当前用户或查找用户后获取用户信息

   // 获取当前登录用户

   // u,_:=user.Current()

   // Lookup()参数是用户名,按照用户名查找指定用户对象

   // 注意:必须使用完整名称不可以只写zhang

   u, _ := user.Lookup(`LAPTOP-M7D47U95\zhang`)

   fmt.Println(u.Name)

   fmt.Println(u.Gid)

   fmt.Println(u.HomeDir)

   fmt.Println(u.Uid)

   fmt.Println(u.Username)

你可能感兴趣的:(golang,学习,golang,开发语言,后端,网络协议)