GO语言学习笔记(3)| 青训营笔记

89 阅读4分钟

[ go 与 golang | 青训营笔记]

这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天, 在学习了go的相关基础知识以后,可以说是初步的了解了go,我在这过程中也记录了一些自己的学习经验以及心得,与大家分享一下。

11. defer

  1. 延迟执行的函数会被压入栈中,return后按照先进后出的顺序调用
  2. 延迟执行的函数其参数会立即求值
  3. 可用于函数异常捕获
func Defer() {
  defer func() {
    err := recover()
    if err != nil {
      fmt.Println(err)
    }
  }()
  n := 0
  fmt.Println(3 / n)
}

func main() {
  Defer()
  fmt.Println("Defer函数之后运行")
}

12. init函数

  1. init函数可以在所有程序执行开始前被调用,并且每个包下可以有多个init函数
  2. init函数先于main函数自动执行
  3. 每个包中可以有多个init函数,每个包中的源文件中也可以有多个init函数
  4. init函数没有输入参数、返回值,也未声明,所以无法引用
  5. 不同包的init函数按照包导入的依赖关系决定执行顺序
  6. 无论包被导入多少次,init函数只会被调用一次,也就是只执行一次
  7. init函数在代码中不能被显示的调用,不能被引用(赋值给函数变量),否则会出现编译错误
  8. Go程序仅仅想要用一个package的init执行,我们可以这样使用:import _ “test_xxxx”,导入包的时候加上下划线就ok了
  9. init函数不应该依赖任何在main函数里创建的变量,因为init函数的执行是在main函数之前的

执行顺序

被依赖的全局变量>>被依赖包的init函数>>...>>main包的全局变量>>main的init函数>>main函数

init能做什么

初始化操作,后面出gorm教程还会使用init函数

13. 包

手动建包

。。。。。。

注意点

  1. 一个包就是一个目录,包名和目录名一致

包的导入

package main

import "fmt"
import "go_study/pkg"

func main() {
  pkg.Pkg()
  fmt.Println("xxx")
}

注意:想要调用别的包的函数,函数名需要大写

package main

import (
  "fmt"
  "go_study/pkg"
)
func main() {
  pkg.Pkg()
  fmt.Println("xxx")
}

包的别名

如果有两个一样的包,可以在导入时起个别名

package main

import (
  "fmt"
  pkg01 "go_study/pkg"
  pkg02 "go_study/pkg"
)

func main() {
  pkg01.Pkg()
  pkg02.Pkg()
  fmt.Println("xxx")
}

全部引入

我们在使用包中的函数、变量的时候,通常都是包名点变量名这样,如果想直接使用变量名,那么需要将包中的内容全部导入

package main

import (
  "fmt"
  . "go_study/pkg"
)

func main() {
  Pkg()
  fmt.Println("xxx")
}

但是这样做会有一些问题,一般是不建议的

如果在当前包中,有重名的函数名或变量,则会编译失败

注意:引入某个包,但不直接使用包里的函数,而是调用该包里面的init函数

package main

import (
  "fmt"
  _ "go_study/pkg"
)

func main() {
  fmt.Println("xxx")
}

14. 数组

一维数组

var arr [3]int = [3]int{7, 8, 9}
var arr1  = [...]int{7, 8, 9}

长度不能为空

使用...可以自动推导长度

package main

import "fmt"

func main() {
  var arr1 [3]int = [3]int{7, 8, 9}
  var arr2 = [...]int{9, 10, 11}

  // 索引
  fmt.Println(arr1[0])
  fmt.Println(arr1[1])
  fmt.Println(arr1[2])
  //fmt.Println(arr1[3])  // 超过索引范围会编译错误

  // 修改元素
  arr2[0] = 11
  arr2[2] = 9
  fmt.Println(arr2)

  // 遍历
  for i := 0; i < len(arr2); i++ {
    fmt.Printf("arr2[%v]=%v\n", i, arr2[i])
  }
  // 第二种遍历
  for index, value := range arr2 {
    fmt.Printf("arr2 index=%v, value=%v\n", index, value)
  }
}

二维数组

var t [3][4]int = [3][4]int{ 
    {1, 2, 3, 4}, 
    {2, 3, 4, 5}, 
    {3, 4, 5, 6}, } 
fmt.Println(t) 

for i, v := range t { 
    for i2, v2 := range v { 
        fmt.Printf("a[%v][%v]=%v\t", i, i2, v2) 
} 
fmt.Println() }

15. 切片

初识切片

有些小伙伴使用数组时会感到不方便,还得自己定义长度

所以golang为我们提供了切片

var arr = [5]int{4, 5, 6, 7, 8}
var slice = arr[0:5] // 切片的两边也是顾头不顾尾
fmt.Println(slice)

array[开始引用的index : 结束引用的index+1]

这里的slice是从arr中切片切出来的

:两边是顾头不顾尾(前闭后开)

左边不写默认是0

右边不写默认是最后一个

切片是对数组的引用

如果改变切片的值,数组中的值也会随之改变

var arr = [5]int{4, 5, 6, 7, 8}
var slice = arr[0:5]
slice[0] = 1
fmt.Println(arr)

指向切片的切片

var arr = [5]int{4, 5, 6, 7, 8}
var slice = arr[0:5]
var s1 = slice[0:2]

slice[0] = 1

fmt.Println(s1)
fmt.Println(slice)
fmt.Println(arr)

切片是引用类型,默认值为nil

var arr []int

fmt.Println(arr == nil) // true

分配内存空间

var s []int
s = make([]int, 3, 5) // 创建一个长度为3, 容量为5的空间
fmt.Println(s)        // [0 0 0]

容量不写,默认等于长度

查看长度和容量

len

cap

由系统自动创建底层数组

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

追加元素

s1 := []int{1,2,3,4}
s = append(s1, 5, 6, 7, 8) // 底层创建了新的数组,不在应用原数组

复制数组

// 复制
var s1 = make([]int, 8, 8)
copy(s1, s)  // 将s复制到s1,复制的个数就是s1的容量大小
fmt.Println(s1)

string与[]byte

str := "hello 世界"
fmt.Printf("[]byte(str)=%s\n", []byte(str))
fmt.Printf("[]byte(str)=%v\n", []byte(str))
// 汉字被分成了三个数字
for i, v := range str{
  fmt.Printf("str[%d]=%c\n", i, v)
}

函数接收多个值

package main

import "fmt"

func Sum(numList ...int) int {
  sum := 0
  for _, v := range numList {
    sum += v
  }
  return sum
}

func main() {
  sum := Sum(1, 3, 4, 5, 6)
  fmt.Println(sum)
}