Go后端开发 -- 条件、循环语句 &&

发布时间:2024年01月05日

Go后端开发 – 条件、循环语句 && defer语句


一、条件语句

1.if … else 语句

语法:

if 布尔表达式 {
   /* 在布尔表达式为 true 时执行 */
} else {
  /* 在布尔表达式为 false 时执行 */
}

与C的if … else语句的区别是条件不用加括号
实例

package main

import "fmt"

func main() {
   /* 局部变量定义 */
   var a int = 100;
 
   /* 判断布尔表达式 */
   if a < 20 {
       /* 如果条件为 true 则执行以下语句 */
       fmt.Printf("a 小于 20\n" );
   } else {
       /* 如果条件为 false 则执行以下语句 */
       fmt.Printf("a 不小于 20\n" );
   }
   fmt.Printf("a 的值为 : %d\n", a);
}

2.switch语句

语法:

switch var1 {
    case val1:
        ...
    case val2:
        ...
    default:
        ...
}
  • switch 语句执行的过程从上至下,直到找到匹配项,匹配项后面也不需要再加 break
  • switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough
  • 变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式
  • 可以同时测试多个可能符合条件的值,使用逗号分割它们,例如:case val1, val2, val3
  • switch语句可以不在switch开头确定判断的条件,可以在case中进行条件判断;

实例:

package Switch

import "fmt"

func Switch() {
	grade := 'B'
	marks := 90

	switch marks {
	case 90:
		grade = 'A'
	case 80:
		grade = 'B'
	case 70, 60, 50:
		grade = 'C'
	default:
		grade = 'D'
	}

	switch {
	case grade == 'A':
		fmt.Println("优秀")
	case grade == 'B':
		fmt.Println("良好")
	case grade == 'C':
		fmt.Println("及格")
	default:
		fmt.Println("不及格")
	}

	fmt.Println("你的等级是:", grade)
}

在这里插入图片描述

  • switch语句还可以被用于type-switch来判断某个 interface 变量中实际存储的变量类型。

Type Switch 语法格式如下

switch x.(type){
    case type:
       statement(s);      
    case type:
       statement(s); 
    /* 你可以定义任意个数的case */
    default: /* 可选 */
       statement(s);
}

实例:

package Switch

import "fmt"

func TypeSwitch() {
	var x interface{}

	switch i := x.(type) {
	case nil:
		fmt.Printf("x的类型: %T\n", i)
	case int:
		fmt.Println("x是int型")
	case float64:
		fmt.Println("x是float64型")
	case func(int) float64:
		fmt.Println("x是func(int)型")
	case bool, string:
		fmt.Println("x是bool或string型")
	default:
		fmt.Println("未知型")
	}
}

在这里插入图片描述

  • fallthrough
    使用 fallthrough 会强制执行后面的 case 语句,包括defaultfallthrough 不会判断下一条case的表达式结果是否为 true。
  • fallthrough只会强制执行后面一条case语句,而不是后面的全部;

实例1:

package Switch

import "fmt"

func Fallthrough() {
	switch {
	case false:
		fmt.Println("1.case条件语句为false")
		fallthrough
	case true:
		fmt.Println("2.case条件语句为true")
		fallthrough
	case false:
		fmt.Println("3.case条件语句为false")
		fallthrough
	case true:
		fmt.Println("4.case条件语句为true")
		fallthrough
	case false:
		fmt.Println("5.case条件语句为false")
		fallthrough
	default:
		fmt.Println("6.默认case")
	}
}

在这里插入图片描述
实例2:

package Switch

import "fmt"

func Fallthrough() {
	switch {
	case false:
		fmt.Println("1.case条件语句为false")
		fallthrough
	case true:
		fmt.Println("2.case条件语句为true")
		fallthrough
	case false:
		fmt.Println("3.case条件语句为false")
		//fallthrough
	case true:
		fmt.Println("4.case条件语句为true")
		//fallthrough
	case false:
		fmt.Println("5.case条件语句为false")
		//fallthrough
	default:
		fmt.Println("6.默认case")
	}
}

在这里插入图片描述

3.select语句

select 是 Go 中的一个控制结构,类似于 switch 语句。

  • select 语句只能用于通道操作每个 case 必须是一个通道操作,要么是发送要么是接收。
  • select 语句会监听所有指定的通道上的操作,一旦其中一个通道准备好就会执行相应的代码块。
  • 如果多个通道都准备好,那么 select 语句会随机选择一个通道执行。如果所有通道都没有准备好,那么执行 default 块中的代码。

Go 编程语言中 select 语句的语法如下:

select {
  case <- channel1:
    // 执行的代码
  case value := <- channel2:
    // 执行的代码
  case channel3 <- value:
    // 执行的代码

    // 你可以定义任意数量的 case

  default:
    // 所有通道都没有准备好,执行的代码
}
  • 每个 case 都必须是一个通道
  • 所有 channel 表达式都会被求值
  • 所有被发送的表达式都会被求值
  • 如果任意某个通道可以进行,它就执行,其他被忽略。
  • 如果有多个 case 都可以运行,select 会随机公平地选出一个执行,其他不会执行。
    否则:
    • 如果有 default 子句,则执行该语句。
    • 如果没有 default 子句,select 将阻塞,直到某个通道 可以运行;Go 不会重新对 channel 或值进行求值。

实例1:

package Select

import (
	"fmt"
	"time"
)

func Select() {
	c1 := make(chan string)
	c2 := make(chan string)

	go func() {
		time.Sleep(1 * time.Second)
		c1 <- "one"
	}()
	go func() {
		time.Sleep(2 * time.Second)
		c2 <- "two"
	}()

	for i := 0; i < 2; i++ {
		select {
		case msg1 := <-c1:
			fmt.Println("received", msg1)
		case msg2 := <-c2:
			fmt.Println("received", msg2)
		}
	}
}

在这里插入图片描述

  • 以上实例中,我们创建了两个通道 c1 和 c2。
    select 语句等待两个通道的数据。如果接收到 c1 的数据,就会打印 “received one”;如果接收到 c2 的数据,就会打印 “received two”。

实例2:

package Select

import (
	"fmt"
	"time"
)

func Select1() {
	c1 := make(chan string)
	c2 := make(chan string)

	go func() {
		time.Sleep(1 * time.Second)
		c1 <- "one"
	}()
	go func() {
		time.Sleep(2 * time.Second)
		c2 <- "two"
	}()

	for i := 0; i < 2; i++ {
		select {
		case msg1 := <-c1:
			fmt.Println("received", msg1)
		case msg2 := <-c2:
			fmt.Println("received", msg2)
		}
	}
}

func Select2() {
	// 定义两个通道
	ch1 := make(chan string)
	ch2 := make(chan string)

	// 启动两个 goroutine,分别从两个通道中获取数据
	go func() {
		for {
			ch1 <- "from 1"
		}
	}()
	go func() {
		for {
			ch2 <- "from 2"
		}
	}()

	// 使用 select 语句非阻塞地从两个通道中获取数据
	for {
		select {
		case msg1 := <-ch1:
			fmt.Println(msg1)
		case msg2 := <-ch2:
			fmt.Println(msg2)
		default:
			// 如果两个通道都没有可用的数据,则执行这里的语句
			fmt.Println("no message received")
		}
	}
}

在这里插入图片描述

  • 以上实例中,我们定义了两个通道,并启动了两个协程(Goroutine)从这两个通道中获取数据。在 main 函数中,我们使用 select 语句在这两个通道中进行非阻塞的选择,如果两个通道都没有可用的数据,就执行 default 子句中的语句。
    以上实例执行后会不断地从两个通道中获取到的数据,当两个通道都没有可用的数据时,会输出 “no message received”。

二、循环语句

1.for循环

for 循环是一个循环控制结构,可以执行指定次数的循环。
Go 语言的 For 循环有 3 种形式,只有其中的一种使用分号。

  • 和 C 语言的 for 一样:
for init; condition; post { }

init: 一般为赋值表达式,给控制变量赋初值;
condition: 关系表达式或逻辑表达式,循环控制条件;
post: 一般为赋值表达式,给控制变量增量或减量。

  • 和 C 的 while 一样:
for condition { }
  • 和 C 的 for(;;) 一样:
for { }
  • for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。格式如下:
for key, value := range oldMap {
    newMap[key] = value
}

以上代码中的 key 和 value 是可以省略。
如果只想读取 key,格式如下:

for key := range oldMap

或者这样:

for key, _ := range oldMap

如果只想读取 value,格式如下:

for _, value := range oldMap

实例1:

package For

import "fmt"

func For() {
	sum := 0
	for i := 1; i <= 10; i++ {
		sum += i
	}
	fmt.Println(sum)
}

在这里插入图片描述

实例2:

package For

import "fmt"

func While() {
	sum := 1

	for sum <= 10 {
		sum += sum
	}
	fmt.Println(sum)
}

在这里插入图片描述

  • 以上实例是将init 和 post参数去掉,相当于是一个while循环

实例3:

package For

import "fmt"

func main() {
   sum := 0
   for {
      sum++ // 无限循环下去
   }
   fmt.Println(sum) // 无法输出
}
  • for后面什么也不跟就是无限循环;

实例4:

package For

import "fmt"

func Range() {
	strings := []string{"google", "runoob"}
	for i, s := range strings {
		fmt.Println(i, s)
	}

	numbers := [6]int{1, 2, 3, 5}
	for i, x := range numbers {
		fmt.Printf("第 %d 位 x 的值 = %d\n", i, x)
	}
}

在这里插入图片描述

  • For-each range 循环可以对字符串、数组、切片等进行迭代输出元素。

实例5:

package For

import "fmt"

func Range() {
	map1 := make(map[int]float32)
    map1[1] = 1.0
    map1[2] = 2.0
    map1[3] = 3.0
    map1[4] = 4.0
   
    // 读取 key 和 value
    for key, value := range map1 {
      fmt.Printf("key is: %d - value is: %f\n", key, value)
    }

    // 读取 key
    for key := range map1 {
      fmt.Printf("key is: %d\n", key)
    }

    // 读取 value
    for _, value := range map1 {
      fmt.Printf("value is: %f\n", value)
    }
}

在这里插入图片描述

  • For-each range也可以读取map中的k和v

三、defer语句

1.defer语句的作用

defer语句被用于预定对一个函数的调用。可以把这类被defer语句调用的函数称为延迟函数。
defer语句会将其后的函数调用推迟到当前所在函数体的生命周期结束后再执行,类似于c++的析构函数。这个特性常用于处理成对的操作,如打开/关闭文件、获取/释放锁、连接/断开连接等确保资源被适当地释放,即使在发生错误或提前返回的情况下也能保证执行。
defer作用:

  • 释放占用的资源
  • 捕捉处理异常
  • 输出日志

如果一个函数中有多个defer语句,它们会以LIFO(后进先出)的顺序执行。
在这里插入图片描述
实例

package Defer

import "fmt"

func Defer() {
	defer fmt.Println("main end1")
	defer fmt.Println("main end2")

	fmt.Println("0")
	fmt.Println("1")
}

在这里插入图片描述

  • defer语句调用的函数会在当前函数体生命周期结束后再出栈执行,多个defer语句调用函数的顺序类似于堆栈,后进先出;

2.defer和return的先后顺序

package Defer

import "fmt"

func deferFunc() int {
	fmt.Println("defer func called...")
	return 0
}

func returnFunc() int {
	fmt.Println("return func caller...")
	return 0
}

func ReturnAndDefer() int {
	defer deferFunc()
	return returnFunc()
}

在这里插入图片描述

  • 由于defer语句会将其后的函数调用推迟到当前所在函数体的生命周期结束后再出栈,而return语句是在}之前的,因此return会先执行,defer再执行;

3.recover错误拦截

运行时panic异常一旦被引发就会导致程序崩溃。

  • Go语言提供了专用于“拦截”运行时panic的内建函数recover它可以是当前的程序从运行时panic的状态中恢复并重新获得流程控制权
  • 注意:recover只有在defer调用的函数中有效。

实例1:

package main

import "fmt"

func recoverFunc(i int) {
	//定义10个元素的数组
	var arr [10]int
	//错误拦截要在产生错误前设置
	//这里使用匿名函数进行错误拦截,在进行defer调用
	//相当于这个匿名函数在recoverFunc的生命周期结束后才被调用
	defer func() {
		//设置recover拦截错误信息
		err := recover()
		//产生panic异常,打印错误信息
		if err != nil {
			fmt.Println(err)
		}
	}()
	//根据函数参数为数组元素赋值
	//如果i的值超过数组下标 会报错误:数组下标越界
	arr[i] = 2
}

func main() {
	recoverFunc(10)
	fmt.Println("程序继续执行...")
}

在这里插入图片描述

  • 在上述实例中,我们定义了一个匿名函数来拦截panic,并用defer去掉用,这个匿名函数会在当前recoverFunc的生命周期结束后进行调用,recoverFunc函数中出现了数组越界的错误,之后被recover()拦截了,程序得以继续执行;

实例2:

package main

import "fmt"

func handlePanic() {
    if r := recover(); r != nil {
        fmt.Println("Recovered:", r)
    }
}

func performTask() {
    defer handlePanic()

    fmt.Println("Performing some task...")
    panic("Oops! Something went wrong!")
    fmt.Println("Task completed.")
}

func main() {
    performTask()
    fmt.Println("Main function continues.")
}

在这里插入图片描述

  • 在以上的实例中,panic函数调用后,异常被引发,当前performTask函数执行结束,defer调用的函数开始出栈,执行handlePanic函数,panic异常引发后,在handlePanic函数中recover拦截了异常,打印出错误信息,并让main函数继续运行。
文章来源:https://blog.csdn.net/kissland96166/article/details/135364434
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。