[ go 与 golang | 青训营笔记]
这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天, 在学习了go的相关基础知识以后,可以说是初步的了解了go,我在这过程中也记录了一些自己的学习经验以及心得,与大家分享一下。
11. defer
- 延迟执行的函数会被压入栈中,return后按照先进后出的顺序调用
- 延迟执行的函数其参数会立即求值
- 可用于函数异常捕获
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函数
- init函数可以在所有程序执行开始前被调用,并且每个包下可以有多个init函数
- init函数先于main函数自动执行
- 每个包中可以有多个init函数,每个包中的源文件中也可以有多个init函数
- init函数没有输入参数、返回值,也未声明,所以无法引用
- 不同包的init函数按照包导入的依赖关系决定执行顺序
- 无论包被导入多少次,init函数只会被调用一次,也就是只执行一次
- init函数在代码中不能被显示的调用,不能被引用(赋值给函数变量),否则会出现编译错误
- Go程序仅仅想要用一个package的init执行,我们可以这样使用:import _ “test_xxxx”,导入包的时候加上下划线就ok了
- init函数不应该依赖任何在main函数里创建的变量,因为init函数的执行是在main函数之前的
执行顺序
被依赖的全局变量>>被依赖包的init函数>>...>>main包的全局变量>>main的init函数>>main函数
init能做什么
初始化操作,后面出gorm教程还会使用init函数
13. 包
手动建包
。。。。。。
注意点
- 一个包就是一个目录,包名和目录名一致
包的导入
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)
}