美文网首页
go入门(三)

go入门(三)

作者: 靈08_1024 | 来源:发表于2019-04-24 19:11 被阅读0次

    本文以流程控制、运算等为主。说一下go中基本的语法糖。

    运算符

    go中的运算符和java中的基本一致,但没有++a,--a这种形式的,只有a++,a--这种形式的。其他都一样。

    go中比较特殊的一点是,字符串是不能数字直接拼接的。需要采用strconv包工具类。
    示例如下:

        //string到int
        i, e := strconv.Atoi("33")
        //string到int64
        int643, e := strconv.ParseInt("233", 10, 64)
        fmt.Println(e)
        //int到string
        string1 := strconv.Itoa(33)
        //int64到string
        string2 := strconv.FormatInt(99, 10)
    
        fmt.Println(i, int643, e, string1, string2)
    

    可以在下面的提示中看到,所有的入参中,入参第一位是要转换的字符串,入参是base的填10,表示十进制,第三位是bitSize,填写64或者32,表示64或32位。
    在返回的参数中,第一位是你需要的值,第二位是异常信息,正常为<nil>,表示无错误。

    image.png

    流程控制

    go中的流程控制有以下几种:

    • 分支流转类:if、if-else、if-else if、switch、goto、select、defer
    • 循环类:for、break、continue
    • 异常类:error、panic

    if系列

    该部分包括所有的if条件语句。go中的if和java中if都是差不多,有一点区别,我们可以从下面的例子中观察一下:

        if num := 6; num > 5 {
            fmt.Println(num, "大于5")
        } else {
            fmt.Println(num, "不大于5")
        }
    

    可以发现,相较于java,有如下特点:

    • if不带(),而且强制带{}(java中一行代码可以不带{}).后面的所有都是如此要求,相对比与java。包括for,switch等。
    • if中可以支持对象声明。

    for

    先来看一个一般的示例:

        for num := 0; num < 10; num++ {
            fmt.Println(num)
        }
    

    这是一个一般输出0到9的例子。

        for i, v := range "08,你好!" {
            fmt.Println( i,string(v))
        }
    

    输出结果:

    0 0
    1 8
    2 ,
    3 你
    6 好
    9 !

    前面的i是所处的字节的下标,后面的对应的字符(UTF-8是3个字节)。
    注意,此处如果直接输出v,得到的是ASCII值。

    goto

    goto就是跳转到指定代码段:

        fmt.Println("我要执行了")
        goto Two
        fmt.Println("我是中间代码")
    One:
        fmt.Println("我是One")
        fmt.Println("hhhhh")
        goto Third
    Two:
        fmt.Println("我是two")
        goto One
    Third:
        fmt.Println("我是Third")
    

    输出结果:

    我要执行了
    我是two
    我是One
    hhhhh
    我是Third

    注意:上面的代码如果去掉Third代码段,会造成死循环,因为读完One后会顺序读到Two代码段,而Two又指向One。

    select

    select和switch类似,但是select是针对于IO的操作。
    示例代码:

    i1, i2 := 1, 2
        c1 := make(chan int, 1)
        c2 := make(chan int, 2)
        c3 := make(chan int, 3)
    
        go func() { c1 <- i1 }()
    
        select {
        case i1 = <-c1:
            fmt.Println("接收给", i1, "从c1", c1)
    
        case c2 <- i2:
            fmt.Println("发送 ", i2, " 到 c2")
    
        case i3, ok := <-c3:
            if ok {
                fmt.Println("从c3接收到i3", i3)
            } else {
                fmt.Println("c3关闭!")
            }
        default:
            fmt.Println("nothing")
        }
    

    当某个通道有IO操作时,会执行某条IO后面的代码。如果都有,则随机执行。
    上面四种情况:
    1)第一个是在i1接收从c1管道发来的变量,是从管道接收值的操作;
    2)第二个是将i2的值发送给c2,是给管道塞值的操作;
    3)从管道中接收值,如果成功,ok为true,否则为false。
    4)默认的操作,如果上面都不走,则执行该条操作。如果不设置default,可能会导致程序阻塞,直到有case通过,否则一直阻塞。

    defer

    defer关键字其后跟随函数或者方法。通过下面的例子来了解一下defer的作用:
    示例一

    func demo1(path string) ([]byte, error) {
        file, err := os.Open(path)
        if err != nil {
            return nil, err
        }
        defer file.Close()
        return ioutil.ReadAll(file)
    }
    

    此处我们用defer来关闭流。
    示例二

    func demo2() {
        f := func(i int) int {
            fmt.Println(i)
            return i * 10
        }
        for i := 1; i < 4; i++ {
            defer fmt.Println(f(i))
        }
    }
    

    输出结果为:

    1
    2
    3
    30
    20
    10

    可以看到defer没有影响其指定的函数内部的输出,只影响了函数结果的倒序输出。可以得出结论,defer的函数会按顺序进行计算,然后将结果存在栈中,FILO(先进后出),从而输出如上结果。有兴趣的同学可以实验。
    示例三
    因为我们发现defer和java中的finally有相似之处,都是在代码尾部执行的。但是defer可以写在任何地方,finally有指定的位置。我们需要测试一下defer是否可以改变程序输出的结果。

    func demo3()Dog{
        dog:=Dog{"ZZ",3}
        defer demo31(&dog)
        return dog
    }
    func demo31(dog *Dog) {
        dog.age=7
    }
    

    输出demo1的结果,发现demo1的结果如下:

    {ZZ 3}

    得出结论,defer不会影响最终的结果。

    总结:defer有以下功能:

    • 关闭文件流;
    • 使代码最后执行且倒序输出结果;
    • 不影响程序最终的结果。

    error

    在java中,如果出现异常,我们的关键字是Exception。那么在go中,我们的关键字是error。在前面我们也陆陆续续的接触到了error,例如在本节最上面的运算符那里,涉及到了error:

    int643, e := strconv.ParseInt("233", 10, 64)
    

    返回的e就是error,如果e为<nil>,则说明一切正常。
    还有defer章节的demo1,也是里面的error是error类型。

    异常的创建
    go中自定义了许多异常,在有些时候,我们需要自定义一些异常。使用的语法是errors.New("...")

    func main() {
        i, e := sqrt(0)
        fmt.Println(i, e)
    }
    
    func sqrt(num int) (int, error) {
        if num == 0 {
            return 0, errors.New("The num is invalid!")
        }
        return num * num, nil
    }
    

    go不像java那样去catch异常,它需要你去判断异常是否为nil。

    panic

    在go中有panic,叫运行时恐慌。他可以层层传递,如果不处理,就会终止程序。
    在第二节的通道部分,我们就见识过panic了。

    panic是go中的内建函数。在go中有另外一个与panic相对的内建函数——recover()。panic是产生恐慌,而recover是恢复恐慌。而recover函数只能在defer语句中使用,因为defer语句是确定在代码末执行的。此处写一个通用的defer示例:

    defer func() {
        if p := recover(); p != nil {
            fmt.Printf("Fatal error: %s\n", p)
        }
    }()
    

    下面是手动创建panic并恢复的例子:

    func bFunc() {
        fmt.Println("Enter bFunc")
        panic(errors.New("~~~~~~~~panic~~~~~~~!"))
        fmt.Println("Quit bFunc")
    
    }
    
    func aFunc() {
        fmt.Println("Enter aFunc")
        bFunc()
        fmt.Println("Quit aFunc")
    
    }
    
    func main() {
        defer func() {
            if p := recover(); p != nil {
                fmt.Printf("Fatal error: %s\n", p)
            }
        }()
        fmt.Println("enter main")
        aFunc()
        fmt.Println("out main")
    }
    

    输出如下:

    enter main

    Enter aFunc
    Enter bFunc
    Fatal error: ~~~~~~~panic~~~~~~!

    参考
    https://segmentfault.com/a/1190000006815341`
    https://www.imooc.com/learn/345

    相关文章

      网友评论

          本文标题:go入门(三)

          本文链接:https://www.haomeiwen.com/subject/fvusgqtx.html