Go 语言入门指南:基础语法和常用特性解析 | 青训营

51 阅读5分钟

由于在课程笔记中我已经整理了大部分课程中讲述的基本语法和常用特性,因此我在整理对未讲授的部分进行拓展:

Slice详解

Go 语言切片是对数组的抽象。

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

定义切片

你可以声明一个未指定大小的数组来定义切片:

var identifier []type

切片不需要说明长度。 或使用make()函数来创建切片:

var slice1 []type = make([]type, len)

也可以简写为

slice1 := make([]type, len)

也可以指定容量,其中 capacity 为可选参数。

make([]T, length, capacity)

这里 len 是数组的长度并且也是切片的初始长度。

切片初始化

s :=[] int {1,2,3 }

直接初始化切片, []  表示是切片类型, {1,2,3}  初始化值依次是 1,2,3,其 cap=len=3

s := arr[:]

初始化切片 s,是数组 arr 的引用。

s := arr[startIndex:endIndex]

将 arr 中从下标 startIndex 到 endIndex-1 下的元素创建为一个新的切片。

s := arr[startIndex:]

默认 endIndex 时将表示一直到arr的最后一个元素。

s := arr[:endIndex]

默认 startIndex 时将表示从 arr 的第一个元素开始。

s1 := s[startIndex:endIndex]

通过切片 s 初始化切片 s1。

s :=make([]int,len,cap)

通过内置函数 make()  初始化切片s[]int 标识为其元素类型为 int 的切片。

len() 和 cap() 函数

  • 切片是可索引的,并且可以由 len() 方法获取长度。
  • 切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。

Defer语句

在Go语言中,defer语句用于在函数返回之前执行一些操作。这些操作可能包括资源释放、文件关闭、锁解锁等。defer语句通常用于确保某些操作在函数返回前一定会被执行,不管函数是否发生了错误或提前返回。

使用defer关键字,可以在函数中的任意位置注册一个需要在函数退出时执行的操作。defer语句后面跟随一个函数调用。

func doSomething() {

    fmt.Println("Doing something...")

    defer fmt.Println("Operation completed.")

}

在上面的例子中,当doSomething函数执行完成时,无论函数是正常结束还是发生了错误,Operation completed.都会被打印出来。

defer 的执行顺序

如果在函数中有多个defer语句,它们的执行顺序与注册的顺序相反。即最后注册的defer语句将最先执行,最先注册的defer语句将最后执行。

func printMessage(msg string) {

    defer fmt.Println("Deferred 2")

    defer fmt.Println("Deferred 1")

    fmt.Println(msg)

}

 

func main() {

    printMessage("Hello, World!")

}

输出结果为:
Hello, World!
Deferred 1
Deferred 2

在注册defer语句时,会对其参数进行求值,但实际执行是在函数返回前。这可能会导致一些潜在的问题,例如当参数是函数调用时,可能导致不符合预期的结果。

func main() {
    x := 0
    defer fmt.Println(x) // 输出 0,因为此时 x 的值为 0
    x++
}

常见用途:

  • 关闭文件:在打开文件后使用defer语句关闭文件,确保文件在函数返回前被关闭,从而避免资源泄漏。
  • 释放锁:在使用锁时,可以在加锁后使用defer语句来释放锁,确保锁的正确使用。
  • 数据库连接的关闭:在使用数据库连接时,使用defer语句关闭连接,确保连接得到释放。
  • 总之,defer语句是Go语言中一种非常有用的机制,它能够帮助我们在函数返回前执行一些必要的清理和收尾操作,提高代码的健壮性和可维护性。

垃圾回收gc

三色标记法

  • 白色:垃圾对象
  • 灰色:被标记,但其对象下的属性未被完全标记
  • 黑色:被标记,且对象下的属性也被完全标记

步骤:

  1. 只要创建新对象,默认都标记为“白色”

image.png

  1. 每次GC开始回收,从根节点遍历所有对象,将遍历到的对象从“白色”标记为“灰色”

image.png

  1. 遍历灰色对象,将灰色对象引用的对象全部标记为灰色,再将该灰色对象标记为黑色,重复操作直到没有灰色对象

image.png

  1. 回收所有白色对象

Bug:当一个灰色对象C和一个白色对象D断开引用(D应该被回收)此时一个黑色对象A(已经被标记扫描后,其引用的应该全为黑)再被扫描之后再次引用了D,此时D本应该不会被回收,但是它错过了A的扫描时间,因此还是会被回收,这样就出现了bug。

解决方法:

  1. 强三色不变式:破坏条件一、不允许黑色对象引用白色对象
  2. 弱三色不变式:破坏条件二、黑色可以引用白色对象,但必须保证其必须存在灰色对象的引用(这样才能保证其能被再次扫描)

屏障机制:(只能用于堆)

插入屏障:在对象别引用时将被引用对象强制变成灰色(强三色不变,黑色不会引用白色)

删除屏障:断开引用时将被断开的标记为灰色(弱三色不变式)