1、if-else

条件语句模型:

if 条件 1 {
  分支 1
} else if 条件 2 {
  分支 2
} else if 条件 ... {
  分支 ...
} else {
  分支 else
}

Go编译器,对于 { 和 } 的位置有严格的要求,它要求 else if (或 else)和 两边的花括号,必须在同一行。
由于 Go是 强类型,所以要求你条件表达式必须严格返回布尔型的数据(nil 和 0 和 1 都不行

这里有一个高级写法,可以允许先运行一个表达式,获取变量后再进行判断

func main() {
    if age := 20;age > 18 {
        fmt.Println("已经成年了")
    }
}

2、switch-case

语句模型

switch 表达式 {
    case 表达式1:
        代码块
    case 表达式2:
        代码块
    case 表达式3:
        代码块
    case 表达式4:
        代码块
    case 表达式5:
        代码块
    default:
        代码块
}

拿 switch 后的表达式分别和 case 后的表达式进行对比,只要有一个 case 满足条件,就会执行对应的代码块,然后直接退出 switch - case ,如果不相等就判断下一个case,如果 一个都没有满足,才会执行 default 的代码块。

一个case后面可以接多个条件
case 后可以接多个条件,多个条件之间是 的关系,用逗号相隔。

func main() {
    month := 2

    switch month {
    case 3, 4, 5:
        fmt.Println("春天")
    case 6, 7, 8:
        fmt.Println("夏天")
    case 9, 10, 11:
        fmt.Println("秋天")
    case 12, 1, 2:
        fmt.Println("冬天")
    default:
        fmt.Println("输入有误...")
    }
}
//output
冬天

case 条件常量不能重复
当 case 后接的是常量时,该常量只能出现一次,如果出现多次,在编译的时候会报错

switch 后可接函数
switch 后面可以接一个函数,只要保证 case 后的值类型与函数的返回值 一致即可。

// 判断一个同学是否有挂科记录的函数
// 返回值是布尔类型
func getResult(args ...int) bool {
    for _, i := range args {
        if i < 60 {
            return false
        }
    }
    return true
}

func main() {
    chinese := 80
    english := 50
    math := 100

    switch getResult(chinese, english, math) {
    // case 后也必须 是布尔类型
    case true:
        fmt.Println("该同学所有成绩都合格")
    case false:
        fmt.Println("该同学有挂科记录")
    }
}

switch 可不接表达式
switch 后可以不接任何变量、表达式、函数。
当不接任何东西时,switch - case 就相当于 if - else if - else

score := 30
switch {
    case score >= 95 && score <= 100:
        fmt.Println("优秀")
    case score >= 80:
        fmt.Println("良好")
    case score >= 60:
        fmt.Println("合格")
    case score >= 0:
        fmt.Println("不合格")
    default:
        fmt.Println("输入有误...")
}

switch 的穿透能力
正常情况下 switch - case 的执行顺序是:只要有一个 case 满足条件,就会直接退出 switch - case ,如果 一个都没有满足,才会执行 default 的代码块。
但是有一种情况是例外。
那就是当 case 使用关键字 *fallthrough *开启穿透能力的时候。

s := "hello"
switch {
case s == "hello":
    fmt.Println("hello")
    fallthrough
case s != "world":
    fmt.Println("world")
}
//output
hello
world

需要注意的是,fallthrough 只能穿透一层,意思是它让你直接执行下一个case的语句,而且不需要判断条件。

3、for循环

基本模型

for [condition |  ( init; condition; increment ) | Range]
{
   statement(s);
}

for 后面,可以接四种类型的表达式:
1、接一个条件表达式

a := 1
for a <= 5 {
    fmt.Println(a)
    a ++
}
//output
1
2
3
4
5

2、接三个表达式
(1)第一个表达式:初始化控制变量,在整个循环生命周期内,只运行一次;
(2)第二个表达式:设置循环控制条件,当返回true,继续循环,返回false,结束循环;
(3)第三个表达式:每次循完开始(除第一次)时,给控制变量增量或减量。

func main() {
    for i := 1; i <= 5; i++ {
        fmt.Println(i)
    }
}
//output
1
2
3
4
5

3、接一个 range 表达式
遍历一个可迭代的对象
range 后可接数组、切片,字符串等
由于 range 会返回两个值:索引和数据,若你后面的代码用不到索引,需要使用 _ 表示

func main() {
    myarr := [...]string{"world", "python", "go"}
    for _, item := range myarr {
        fmt.Printf("hello, %s\n", item)
    }
}
//output
hello, world
hello, python
hello, go

如果你用一个变量来接收的话,接收到的是索引

func main() {
    myarr := [...]string{"world", "python", "go"}
    for i := range myarr {
        fmt.Printf("hello, %v\n", i)
    }
}
//output
hello, 0
hello, 1
hello, 2

4、不接表达式
类似于C语言中的while循环,不添加表达式就代表一直是true
但是一般我们并不会让程序处于死循环,在满足一定的条件下,可以使用关键字 break 退出循环体,也可以使用 continue 直接跳到下一循环。
这两种都是不添加表达式的写法:

for {
    代码块
}
// 等价于
for ;; {
    代码块
}
func main() {
    var i int = 1
    for {
        if i > 5 {
            break
        }
        fmt.Printf("hello, %d\n", i)
        i++
    }
}
//output
hello, 1
hello, 2
hello, 3
hello, 4
hello, 5

4、goto无条件跳转

goto 后接一个标签,这个标签的意义是告诉 Go程序下一步要执行哪里的代码。
所以这个标签如何放置,放置在哪里,是 goto 里最需要注意的。

goto 标签;
...
...
标签: 表达式;

goto 可以打破原有代码执行顺序,直接跳转到某一行执行代码。

func main() {
    goto flag
    fmt.Println("B")
flag:
    fmt.Println("A")
}
//执行结果,并不会输出 B ,而只会输出 A
A

goto 语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。
循环功能

func main() {
    i := 1
flag:
    if i <= 5 {
        fmt.Println(i)
        i++
        goto flag
    }
}
//output
1
2
3
4
5

使用 goto 实现 类型 break 的效果。

func main() {
    i := 1
    for {
        if i > 5 {
            goto flag
        }
        fmt.Println(i)
        i++
    }
flag:
}
//output
1
2
3
4
5

使用 goto 实现 类型 continue的效果

func main() {
    i := 1
flag:
    for i <= 10 {
        if i%2 == 1 {
            i++
            goto flag
        }
        fmt.Println(i)
        i++
    }
}
//output
2
4
6
8
10

注意事项
goto语句与标签之间不能有变量声明,否则编译错误。

func main() {
    fmt.Println("start")
    goto flag
    var say = "hello oldboy"
    fmt.Println(say)
flag:
    fmt.Println("end")
}
//编译错误
.\main.go:7:7: goto flag jumps over declaration of say at .\main.go:8:6

4、defer 延迟语句

  1. 延迟调用
    defer 的用法很简单,只要在后面跟一个函数的调用,就能实现将这个 xxx 函数的调用延迟到当前函数执行完后再执行。
    defer xxx()
func myfunc() {
    fmt.Println("B")
}

func main() {
    defer myfunc()
    fmt.Println("A")
}
//output
A
B
  1. 即时求值的变量快照
    使用 defer 只是延时调用函数,此时传递给函数里的变量,不应该受到后续程序的影响。

    func main() {
     name := "go"
     defer fmt.Println(name) // 输出: go
    
     name = "python"
     fmt.Println(name)      // 输出: python
    }
    //output
    python
    go

    可见给 name 重新赋值为 python,后续调用 defer 的时候,仍然使用未重新赋值的变量值,就好在 defer 这里,给所有的这是做了一个快照一样。

  2. 多个defer 反序调用--多个defer 是反序调用的,有点类似栈一样,后进先出

    func main() {
     name := "go"
     defer fmt.Println(name) // 输出: go
    
     name = "python"
     defer fmt.Println(name) // 输出: python
    
     name = "java"
     fmt.Println(name)
    }
    //output
    java
    python
    go
  3. defer 与 return 孰先孰后

    var name string = "go"
    func myfunc() string {
     defer func() {
         name = "python"
     }()
    
     fmt.Printf("myfunc 函数里的name:%s\n", name)
     return name
    }
    func main() {
     myname := myfunc()
     fmt.Printf("main 函数里的name: %s\n", name)
     fmt.Println("main 函数里的myname: ", myname)
    }
    //output
    myfunc 函数里的name:go
    main 函数里的name: python
    main 函数里的myname:  go

来一起理解一下这段代码,第一行很直观,name 此时还是全局变量,值还是go
第二行也不难理解,在 defer 里改变了这个全局变量,此时name的值已经变成了 python
重点在第三行,为什么输出的是 go ?
解释只有一个,那就是 defer 是return 后才调用的。所以在执行 defer 前,myname 已经被赋值成 go 了。
5. 为什么要有 defer?

若是没有 defer,你可以写出这样的代码

func f() {
    r := getResource()  //0,获取资源
    ......
    if ... {
        r.release()  //1,释放资源
        return
    }
    ......
    if ... {
        r.release()  //2,释放资源
        return
    }
    ......
    if ... {
        r.release()  //3,释放资源
        return
    }
    ......
    r.release()     //4,释放资源
    return
}

使用了 defer 后,代码就显得简单直接,不管你在何处 return,都会执行 defer 后的函数。

func f() {
    r := getResource()  //0,获取资源

    defer r.release()  //1,释放资源
    ......
    if ... {
        ...
        return
    }
    ......
    if ... {
        ...
        return
    }
    ......
    if ... {
        ...
        return
    }
    ......
    return
}