一、学习指引
二、Ubuntu系统
操作与使用:https://zhuanlan.zhihu.com/p/672688377
linux常用命令:https://blog.csdn.net/m0_46422300/article/details/104645072
三、环境安装和配置
在linux系统中安装go环境:https://www.bilibili.com/video/BV1gf4y1r79E?p=2&vd_source=7ac1840ef91fce196a35d848a3779e29
在linux系统中安装goland:https://blog.csdn.net/sanqima/article/details/113623671
goland常用快捷键:https://zhuanlan.zhihu.com/p/548611534
goland上手:https://zhuanlan.zhihu.com/p/120347718 目前在使用go.mod方式下,不需要进行gopath的设置也行
goroot&gopath&gomodule参见此文:https://blog.csdn.net/qq_38151401/article/details/105729884
四、Go基础夯实
4.1 go语言结构
环境变量配置:
package&module:https://juejin.cn/post/6869570760738865166
4.2 变量和常量
介绍:https://zhuanlan.zhihu.com/p/143087271
注意点:
只允许在函数体内使用语法糖对变量进行声明和赋值
声明了一个局部变量却没有在相同的代码块使用,同样会编译错误
全局变量允许声明但不使用
全局变量的生命周期是程序存活时间
在不发生内存逃逸的情况下,局部变量是函数存活时间
4.3 go语言运算符
介绍:https://segmentfault.com/a/1190000044201272
运算符优先级:
4.4 go语言结构体
介绍:https://blog.csdn.net/m0_37710023/article/details/107657413 https://zhuanlan.zhihu.com/p/654946733
4.5 go语言数组与切片
数组介绍:https://www.runoob.com/go/go-arrays.html
切片介绍:https://zhuanlan.zhihu.com/p/629420925
map介绍:https://blog.csdn.net/weixin_43529465/article/details/129036783
map底层原理:https://zhuanlan.zhihu.com/p/460958342
4.6 go语言条件句
介绍:https://blog.csdn.net/wohu1104/article/details/99006545
if 语句特点:
if 后面的条件判断子句不需要用小括号括起来;
{ 必须放在行尾,和 if 或者 else if 放在一行;
if 后面可以带一个简单的初始化语句,并以分号分割,该简单语句声明的变量的作用域是整个 if 语句块,包括后面的 else if 和 else 分支;
if 分支语句遇到 return 后直接返回,遇到 break 则跳过 break 下方的 if 语句块;
switch语句特点:
switch 和 if 语句一样, switch 后面可以带一个可选的简单的初始化语句;
switch 后面的表达式也是可选的,如果没有表达式, 则 case 子句是一个布尔表达式,而不是一个值,此时就相当于多重 if else 语句;
switch 条件表达式的值不像 C/C++ 语言那样必须限制为整数常量, 表达式不需要为常量,甚至不需要为整数,可以是任意支持相等比较运算的类型变量;
switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上至下逐一测试,直到匹配为止;
switch 语句执行的过程从上至下,直到找到匹配项,匹配项后面也不需要再加 break ;
switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough ;
switch 支持 default 语句, 当所有的 case 分支都不符合时, 执行 default 语句, 并且 default 语句可以放到任意位置,并不影响 switch 的判断逻辑;
4.7 go语言循环
介绍:https://blog.csdn.net/weixin_44211968/article/details/121251817
4.8 go语言函数
介绍:https://www.runoob.com/go/go-functions.html
闭包:(匿名函数+捕获的变量)
Gopackage main import "fmt" func adder() func(int) int { sum := 0 return func(x int) int { fmt.Println("执行前 sum =", sum) sum += x return sum } } func main() { pos := adder() for i := 0; i < 4; i++ { fmt.Println("执行后 sum =", pos(1)) }
以上程序执行过程:
当程序执行main函数时,首先调用adder函数并将返回的闭包函数赋值给pos变量。在adder函数中,定义了一个sum变量,并返回一个闭包函数,闭包函数引用了外部的sum变量。
接着进入for循环,循环4次。每次循环中调用pos(1),传入参数1。闭包函数内部会打印当前sum的值,然后将参数值加到sum上,并返回新的sum值。
下面是程序的运行流程:
i=0 时,执行 pos(1):
输出 "执行前 sum = 0"
sum = sum + 1,即 sum = 0 + 1 = 1
输出 "执行后 sum = 1"
i=1 时,执行 pos(1):
输出 "执行前 sum = 1",之前的sum值为1
sum = sum + 1,即 sum = 1 + 1 = 2
输出 "执行后 sum = 2"
i=2 时,执行 pos(1):
输出 "执行前 sum = 2",之前的sum值为2
sum = sum + 1,即 sum = 2 + 1 = 3
输出 "执行后 sum = 3"
i=3 时,执行 pos(1):
输出 "执行前 sum = 3",之前的sum值为3
sum = sum + 1,即 sum = 3 + 1 = 4
输出 "执行后 sum = 4"
通过闭包函数,sum变量得以保留并且可以在每次调用闭包函数时被修改和访问,形成了类似于对象的状态。这种特性使得闭包函数在Go语言中非常灵活和强大。
adder函数在main函数中被调用并返回闭包函数给pos变量时,adder函数的生命周期会至少持续到程序执行完毕。因为返回的闭包函数中引用了adder函数作用域内的sum变量,所以add函数不会在其作用域结束后立即被销毁,而是会保持活跃状态,直至所有引用它的闭包函数被销毁。(在这个例子中,由于闭包函数持续引用着adder函数作用域内的变量,adder函数的生命周期会持续到所有引用它的闭包函数都被销毁为止。)
4.9 go语言指针
介绍:https://zhuanlan.zhihu.com/p/630590733
make与new的区别:https://c.biancheng.net/view/5722.html#
4.10 go语言方法
介绍:https://blog.csdn.net/wohu1104/article/details/106202918
方法是面向对象的,函数是面向过程的
接口类型不能作为方法的receiver基类,但是可以作为函数的参数和返回值
4.11 go语言接口
介绍:https://juejin.cn/post/7060471869085843487
一个对象只要全部实现了接口中的方法,那么就实现了这个接口(无论是自定义的还是内置的)。换句话说,接口就是一个需要实现的方法列表。一个对象可以实现多个接口,一个接口也可以被多个对象实现。
4.12 go语言error
介绍:https://cloud.tencent.com/developer/article/2232185
4.13 go语言defer
go语言的一个关键字,用在函数或者方法前面,延迟函数的执行:defer func()
延迟函数什么时候被调用:(一定会被执行,无论是否正常退出)
函数return的时候
发生panic的时候
延迟调用的语法规则
defer关键字后面表达式必须是函数或者方法调用
延迟内容不能被括号括起来
多个defer同时出现时,按倒序执行,先定义的后执行
defer的使用场景
资源释放
配合recover捕获异常
defer考点:
代码考察,通过defer修改函数放回值,考察输出。
defer与return的关系:https://cloud.tencent.com/developer/article/1453355(非常好,一看就懂)
return非原子操作,其执行的过程可分为三步:
设置返回值,对于无名返回值,这个返回值保存在一个临时空间中(该空间不同于函数中为局部变量i开辟大空间),defer时看不到这个临时空间(与局部变量i地址不同),只能看到局部变量的地址,所以defer函数中对i的操作也能正常执行,但是因为不是对返回值空间进行的操作,所以返回值只会为局部变量被定义时取得的那个值(因为设置返回值快于defer,故不是defer函数执行后的i值);而对于有名返回值,就保存在已命名的变量中,故defer能看到这个返回值(同一地址),后续的操作也对这个地址的返回值操作。
执行defer语句
将结果返回
4.14 go异常捕获
介绍:https://www.cnblogs.com/open-yang/p/11256858.html
defer嵌套写法的解析:https://juejin.cn/post/7197361396220100663
Go// 第一种 func main() { x := 1 y := 2 defer calcTest("AA", x, calcTest("A", x, y)) x = 10 defer calcTest("BB", x, calcTest("B", x, y)) y = 20 } func calcTest(index string, a, b int) int { ret := a + b fmt.Println(index, a, b, ret) return ret } /* A 1 2 3 B 10 2 12 BB 10 12 22 AA 1 3 4 */ func main() { x := 1 y := 2 defer func() { calcTest("AA", x, calcTest("A", x, y)) }() x = 10 defer func() { calcTest("BB", x, calcTest("B", x, y)) }() y = 20 } /* B 10 20 30 BB 10 30 40 A 10 20 30 AA 10 30 40 */
defer语句后面,如果未使用嵌套写法,这时函数里的参数值都得为确定值(先确定参数,但推迟整个函数的执行),即不会因为后面的修改而影响;如果使用嵌套写法,由于匿名函数无参数,需进入函数体内后才开始确定calcTest里面的参数,故得到的是x,y的最终结果; 所以,例子1执行顺序:先执行输出A→B,然后defer逆序执行输出BB→AA;例子2执行顺序:直接defer逆序执行,顺序为B→BB→A→AA,每一步带入相应的x y值,即可推导出结果。 第一种是非匿名函数写法
初始化变量 x 为 1
初始化变量 y 为 2
执行 defer calcTest("AA", x, calcTest("A", x, y))
这里会先计算 calcTest("A", x, y),即调用 calcTest 函数并传入参数 "A", 1, 2
"A 1 2 3" 被打印出来
然后再计算 calcTest("AA", x, 3),即调用 calcTest 函数并传入参数 "AA", 1, 3
"AA 1 3 4" 被打印出来
注意:defer 语句中的函数调用会被推迟到当前函数执行结束之前才执行
将变量 x 更新为 10
执行 defer calcTest("BB", x, calcTest("B", x, y))
这里会先计算 calcTest("B", x, y),即调用 calcTest 函数并传入参数 "B", 10, 2
"B 10 2 12" 被打印出来
然后再计算 calcTest("BB", x, 12),即调用 calcTest 函数并传入参数 "BB", 10, 12
"BB 10 12 22" 被打印出来
将变量 y 更新为 20
函数执行结束
第二种是匿名函数的写法 由于使用了defer关键字,所以两个匿名函数都会被推迟到函数执行结束之前才执行,由于匿名函数无参数,所以无需提前确定,所以也是到那个时候才开始进入函数体内后才开始确定calcTest的参数。
recover和panic可以总结为以下两点:
recover只能回复当前函数级或以当前函数为首的调用链中的函数中的panic(),恢复后当前调用recover的函数结束,但是调用此函数的函数继续执行
函数发生了panic之后会一直向上传递,如果直至main函数都没有recover(),程序将终止,如果是碰见了recover(),将被recover捕获
recover必须搭配defer来使用,否则panic捕获不到;
defer一定要在可能引发panic的语句之前定义;
4.15 依赖管理
介绍:https://blog.csdn.net/qq_31930499/article/details/101108056
下载失败可看:https://zhuanlan.zhihu.com/p/525542452
相关阅读
发表评论