先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注go)
正文
返回两个值:索引和数据
,若你后面的代码用不到索引,需要使用 _ 表示 。
name :=“长得帅”
//索引 i , 字符ch name字符串
for i,ch := range name{
fmt.Println(i,ch)
}
占位符 “_”
name :=“长得帅”
//索引 i , 字符ch name字符串
for _,ch := range name{
fmt.Println(ch)
}
- 建议用引用类型,其
底层
数据不会被复制
package main
func main() {
s := []int{1, 2, 3, 4, 5}
for i, v := range s { // 复制 struct slice { pointer, len, cap }。
if i == 0 {
s = s[:3] // 对 slice 的修改,不会影响 range。
s[2] = 100 // 对底层数据的修改。
}
println(i, v)
}
}
//结果
0 1
1 2
2 100
3 4
4 5
===========================================================================
- continue
跳过本次循环
- break
break lable
跳出循环,可以如下,跳出多层循环
END
for i :=1 ;i<=100 ;i++{
break END
}
- return用在
方法或函数
中,表示终止所在的方法或函数(method与function)
return在main函数中,表示终止main函数,终止程序
for i := 0; i <= 10; i++ {
if i == 5 {
return //直接退出main函数了
}
fmt.Printf(“本次循环次数:%d\n”, i)
}
//永远走不带这里了,第五次for循环时候,直接return了
fmt.Println(“循环结束,走到了我”)
=============================================================================
goto
标签:大写的英文字母,放在最左面
可以一次性跳出多层循环
goto 标签A
标签A:
用跳转实现for循环
goto语句与标签之间不能有变量声明,否则编译错误
.\main.go:7:7: goto flag jumps over declaration of say at .\main.go:8:6
=================================================================================
在后面跟一个函数的调用,就能实现将这个 xxx 函数的调用延迟
到当前函数执行完后再执行。
defer xxx()
import “fmt”
func myfunc() {
fmt.Println(“B”)
}
func main() {
defer myfunc()
fmt.Println(“A”)
}
//输出如下
A
B
import “fmt”
func main() {
defer fmt.Println(“B”)
fmt.Println(“A”)
}
使用 defer 只是延时调用函数,此时传递给函数里的变量,不应该受到后续程序的影响
。
比如这边的例子
import “fmt”
func main() {
name := “go”
defer fmt.Println(name) // 输出: go
name = “python”
fmt.Println(name) // 输出: python
}
// 输出
python
go
可见给 name 重新赋值为 python,后续调用 defer 的时候,仍然使用未重新赋值
的变量值,就好在 defer 这里,给所有的这是做了一个快照一样。
如果 defer 后面跟的是匿名函数
,情况会有所不同, defer 会取到最后的变量值
package main
import “fmt”
func main() {
name := “go”
defer func(){
fmt.Println(name) // 输出: python
}()
name = “python”
fmt.Println(name) // 输出: python
}
当我们在一个函数里使用了 多个defer,那么这些defer 的执行函数是如何的呢?
import “fmt”
func main() {
name := “go”
defer fmt.Println(name) // 输出: go
name = “python”
defer fmt.Println(name) // 输出: python
name = “java”
fmt.Println(name) //输出:java
}
//输出
java
python
go
输出如下,可见 多个defer 是反序调用
的,有点类似栈
一样,后进先出。
defer 和 return
到底是哪个先调用?
import “fmt”
var name string = “go”
func myfunc() string {
defer func() {
name = “python”
}()
fmt.Printf(“myfunc 函数里的name:%s\n”, name)
return name
}
func main() {
myname := myfunc()
fmt.Printf(“main 函数里的name: %s\n”, name)
fmt.Println("main 函数里的myname: ", myname)
}
//输出
myfunc 函数里的name:go
main 函数里的name: python
main 函数里的myname: go
第一行name 此时还是全局变量,值还是go
第二行也不难理解,在 defer 里改变了这个全局变量,此时name的值已经变成了 python
第三行, defer 是return 后才调用的
。所以在执行 defer 前,myname 已经被赋值成 go 了。
把这个放在 defer 执行的函数放在 return 那里执行不就好了。
但是当一个函数里有多个 return
时,你得多调用好多次这个函数,代码就臃肿起来了。
没有 defer
func f() {
r := getResource() //0,获取资源
…
if … {
r.release() //1,释放资源
return
}
…
if … {
r.release() //2,释放资源
return
}
…
if … {
r.release() //3,释放资源
return
}
…
r.release() //4,释放资源
return
}
用了 defer 后,代码就显得简单直接,不管你在何处 return,都会执行 defer 后的函数
。
func f() {
r := getResource() //0,获取资源
defer r.release() //1,释放资源
…
if … {
…
return
}
…
if … {
…
return
}
…
if … {
…
return
}
…
return
}
===========================================================================================
-
panic:
抛
出异常,使程序崩溃 -
recover:
捕获
异常,恢复程序或做收尾工作
panic、recover 参数类型为 interface{}
,因此可抛出任何类型对象。
revocer 调用后,抛出的 panic 将会在此处终结
,不会再外抛,但是 recover,并不能任意使用,它有强制要求,必须得在 defer 下
才能发挥用途。
-
利用recover处理panic指令,defer 必须放在 panic 之前定义,另外 recover 只有在 defer 调用的函数中才有效。否则当panic时,recover无法捕获到panic,无法防止panic扩散。
-
recover 处理异常后,逻辑并不会恢复到 panic 那个点去,函数跑到 defer 之后的那个点。
-
多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用。
手动触发宕机,是非常简单的一件事,只需要调用 panic
这个内置函数
package main
func main() {
panic(“crash”)
}
运行后,直接报错宕机
$ go run main.go
go run main.go
panic: crash
goroutine 1 [running]:
main.main()
E:/Go-Code/main.go:4 +0x40
exit status 2
发生了异常,有时候就得捕获
这就不得不引出另外一个内建函数 – recover
,它可以让程序在发生宕机后起生回生
。
但是 recover 的使用,有一个条件,就是它必须在 defer 函数中才能生效
,其他作用域下,它是不工作的。
import “fmt”
func set_data(x int) {
defer func() {
// recover() 可以将捕获到的panic信息打印
if err := recover(); err != nil {
fmt.Println(err)
}
}()
// 故意制造数组越界,触发 panic
var arr [10]int
arr[x] = 88
}
func main() {
set_data(20)
// 如果能执行到这句,说明panic被捕获了
// 后续的程序能继续运行
fmt.Println(“everything is ok”)
}
运行后
$ go run main.go
runtime error: index out of range [20] with length 10
everything is ok
通常来说,不应该对进入 panic 宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,
当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。
即使 panic 会导致整个程序退出,但在退出前,若有 defer 延迟函数,还是得执行完 defer
。
但是这个 defer 在多个协程之间是没有效果
,在子协程
里触发 panic
,只能触发自己协程内的 defer,而不
能调用 main 协程里的 defer 函数的。
import (
“fmt”
“time”
)
func main() {
// 这个 defer 并不会执行
defer fmt.Println(“in main”)
go func() {
defer println(“in goroutine”)
panic(“”)
}()
time.Sleep(2 * time.Second)
}
//输出如下
in goroutine
panic:
goroutine 6 [running]:
main.main.func1()
E:/Go-Code/main.go:12 +0x7b
created by main.main
E:/Go-Code/main.go:10 +0xbc
exit status 2
向已关闭的通道发送数据会引发panic
package main
import (
“fmt”
)
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
var ch chan int = make(chan int, 10)
close(ch)
ch <- 1
}
//结果
send on closed channel
=====================================================================
将一个数从个位,十位,百位输出
思路
百位: n/100%10
十位: n/10%10
个位: n/1%10
代码
package main
import “fmt”
func main() {
var n,s int
fmt.Scan(&n)
if n>=999 {
s =4
} else if n>99{
s =3
}else if n>9{
s=2
}else {
s=1
}
for i:=1;i<=s;i++{
// l为10 *i
l :=1
for j:=2;j<=i;j++{
l *=10
}
fmt.Println(n/l%10)
}
}
多次登陆
package main
import “fmt”
func main() {
var name,pwd string
//限制:只能重复登录三次,
var logincache=3
for i :=1;i<=3;i++{
fmt.Println(“请输入用户名”)
fmt.Scanln(&name)
fmt.Println(“请输入密码”)
fmt.Scanln(&pwd)
if name == “qcq” && pwd == “qcq123” {
fmt.Println(“欢迎来着王者荣耀!”)
break
}else {
logincache–
fmt.Printf(“输入错误,你还有%d次机会\n”,logincache)
}
}
}
输出一个正方行
计算100以内所有奇数的和
func main() {
i :=1
num :=0
for i<=100{
num +=i
i+=2
}
fmt.Println(num)
}
给学生成绩评级
func main() {
var n int
fmt.Scanln(&n)
switch{
case n>=90:
fmt.Println(“A”)
case n>=80:
fmt.Println(“B”)
case n>60:
fmt.Println(“C”)
default:
fmt.Println(“你太笨了”)
}
}
打印成发口诀表
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
/ l为10 *i
l :=1
for j:=2;j<=i;j++{
l *=10
}
fmt.Println(n/l%10)
}
}
多次登陆
package main
import “fmt”
func main() {
var name,pwd string
//限制:只能重复登录三次,
var logincache=3
for i :=1;i<=3;i++{
fmt.Println(“请输入用户名”)
fmt.Scanln(&name)
fmt.Println(“请输入密码”)
fmt.Scanln(&pwd)
if name == “qcq” && pwd == “qcq123” {
fmt.Println(“欢迎来着王者荣耀!”)
break
}else {
logincache–
fmt.Printf(“输入错误,你还有%d次机会\n”,logincache)
}
}
}
输出一个正方行
计算100以内所有奇数的和
func main() {
i :=1
num :=0
for i<=100{
num +=i
i+=2
}
fmt.Println(num)
}
给学生成绩评级
func main() {
var n int
fmt.Scanln(&n)
switch{
case n>=90:
fmt.Println(“A”)
case n>=80:
fmt.Println(“B”)
case n>60:
fmt.Println(“C”)
default:
fmt.Println(“你太笨了”)
}
}
打印成发口诀表
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-efphJSdU-1713256724489)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!