go

头像
THZXQ
    阅读 4 分钟

    语言简介

    go是强类型静态语言,也就是说在编译阶段就确定变量类型,并且转换类型需要显示转换,它有如下几个特点:
    1. 跟脚本语言相似,容易上手、容易流行
    2. 底层是c,性能好
    3. 对goroutine调度进行了封装,即在语言层面支持协程,对于开发者,用起来方便

    编译过程

    • go以包的形式管理源程序,每个包至少包含一个go文件。编译的时候会把main包生成可执行文件放到bin目录下,其他包生成xx.a包文件放到pkg目录下。

    可执行程序初始化过程

    • 先执行main包的go文件,对依赖的包递归执行import、常量和变量初始化、init函数流程之后,初始化main自己的常量和变量、再依次执行init函数和main函数,示意图如下(竟然不能直接放图片,?):
      https://github.com/JJAWX/imag...

    go文件基本构成

    package xx

    表明这个go文件是属于哪个包,这条语句是必须写的,因为go是以package的形式管理源文件的

    import xx

    引入当前文件依赖的包,包的路径只可以是相对路径,eg:"xxx/decorator",go查找包的过程是先在$GOROOT/src($GOROOT是go的安装路径,默认在/usr/local/go下)文件夹下找,如果找不到就从$GOPATH/src($GOPATH是go工作空间路径,当前工作代码所在的go文件夹路径)文件下找。

    var/const声明初始化

    整型
        var test int = 123
    布尔
        var test bool = true
    字符串
        var test string = "abc"
    数组(数组必须指定长度,否则属于切片,不赋值,指定长度默认[0,0])
        var test [2]int
        test[0] = 6
        test[1] = 8
    切片(没指定长度的数组或者用make初始化的数组)
        // 添加元素方式一
        var test = make([]string, 0)
        test = append(test, 2)
        // 添加元素方式二
        var test = make([]string, 2)
        test[0] = "123"
        test[1] = "456"
    结构体
        type TestA struct {
            c int
        }
        type Test struct {
            a int
            b string
            c int
            TestA
        }
        var test = Test{6, "aa", 8, TestA{3}} // 或者不初始化:var test Test
    channel
        test := make(chan int)
        go func(){ test <- 1 }()
        fmt.Printf("aaa: %+v", <-test)
    interface{}(一个接口可以拥有任何实现了它所有方法的类型的属性,空接口代表所有对象都实现了它,也就是说用它可以接收任何类型)
        type Test interface{
            Say()
        }
        type TestA struct{}
        func (a TestA) Say(){
            fmt.Printf("hello")
    
        } 
        var testa TestA
        var test Test
        test = testa // 接口类型,
    指针
         var  testPtr *int // 定义了没有分配到任何变量时,为空指针
    map(默认是nil,不初始化,不能存任何东西)
        // 方式一
        var test = make(map[string]string)
        test["a"] = "123"
        
        // 方式二
        var test = map[string]string{}
        test["a"] = "123"
        
        // 方式三
        test := make(map[string]string)
        test["a"] = "123"

    init函数

    初始化一些全局变量,供所属包或者所有包调用

    main函数

    属于main包,是程序的入口,和main包是一一对应的关系。

    其他函数

    业务子逻辑,处理客户端的GET、POST、PUT、DELETE等请求,get请求使用url库对请求参数进行解析,跟它相关的几个方法是:DefaultQuery、Query、QueryArray,;GET、POST、PUT、DELETE请求的话使用的是gin框架的model binding模型,相关的几个方法是:BindWith、BindJSON、Bind、它们底层都使用了MustBindWith这个方法, 这个方法接收两个参数,一个是要解析的struct(ps:解析的对象必须是struct或者是指向struct的指针),另一个是binding,由跟Bind相关的几个函数指定,指定的方式区别如下:

    • BindWith: 可接收两个参数,一个是要解析的对象,一个是binding
    • BindJSON: 只接收一个参数,是要解析的对象,使用的binding是bing.JSON,它对要解析的数据是怎么处理的呢,从req.body中拿出要解析的数据,使用reflect反射对要解析的对象类型进行判断,然后从req.body中取要的数据
    • Bind: 接收一个参数,会根据请求的method和content-type判断使用什么binding来解决。

    逻辑处理完成,需要使用context.JSON(关键:json.Marshal)或context.HTML(ps: 分别接收三个参数:状态码、模版路径、模版数据)将结果返回给客户端。

    go异常捕捉处理

    和异常处理相关的几个关键字:defer(return的时候执行)、recover(捕捉异常)、panic(err)(抛出异常),需要注意的地方是如果子协程不捕捉处理异常,就会报到主线程,由主线程处理,但是子协程会异常退出。

        func example () {
            defer func() {
                if err := recover(); err != nil {
                    fmt.Printf("error: %+v", err)
                } else {
                    fmt.Printf("抛出了一个nil")
                }
            }()
            // panic(nil) // 结果:抛出了一个nil
            panic("fail") // 结果: error: failed
        }

    go并发

    go关键字 + channel

        // 无缓冲通道
        func example () {
            test := make(chan int)
            go func(){
                test <- 1
            }()
            fmt.Printf("channel value: %+v", <-test)
        }
        // 执行结果
        channel value: 1
        
        // 缓冲通道
        func example () {
            test := make(chan int, 2)
            go func(){
                for i := 0; i < 2; i++ {
                    test <- i
                }
                close(test) // 很重要,需要手动关闭,要不然会造成死锁
            }()
            for ch := range test {
                fmt.Printf("channel value: %+v", ch)
                fmt.Println("")
            }
        }
        // 执行结果
        channel value: 0
        channel value: 1

    sync.WaitGroup(例子 + 结果)

        func example () {
            var wg sync.WaitGroup
            for i := 0; i < 2; i++ {
                m := i // 考虑到主线程释放控制权之后协程才执行,缓存i
                wg.Add(1)
                go func(){
                    defer func(){
                        fmt.Println("ending...")
                        wg.Done()
                    }()
                    fmt.Println(fmt.Sprintf("当前循环到哪里了: %+v", m))
                }()
            }
            
            fmt.Println("example...")
            wg.Wait()
        }
        // 执行结果
        example...
        当前循环到哪里了: 0
        ending...
        当前循环到哪里了: 1
        ending...

    THZXQ
    203 声望9 粉丝

    前端工程师