12、做中学 | 初一上期 Golang函数 包 异常
一、认识go函数
1.回忆
还记得刚开始学go的时候,打印的hello world吗?
package mainimport "fmt"func main() {fmt.Println("hello, world!")// hello, world!
}
可以看到我们把"hello, world!"放入到了Println()方法中,那这个Println()方法是怎么定义的呢?
按着Alter键和鼠标进行点击Println(),进入源码
2. 认识
打开了上边的Println()方法代码,我们可以看到函数的整体框架,但都代表什么意思呢?
func 函数名称(形参列表) (返回值列表) {执行语句return 返回值列表
}
- func: 函数出场关键词func(function)
- 函数名称:符合命名规范,见名识意
- 形参列表:函数入参列表(实参为函数调用时传入参数,大致区分下就行)
- 返回值列表:函数输出的列表结果
- 执行语句:函数要执行的功能
- return:进行返回函数处理的结果
好了,根据上边描述,已经有了大概了了解了!
3. 模仿
我想要写一个输入任意俩个数,进行加和,并输出俩数之和!
package mainimport "fmt"func main() {// 调用并打印var result int = mySum(10, 20)fmt.Println("result = ", result)// result = 30
}// 写一个输入任意俩个数,进行加和,并输出俩数之和!
// 1. 俩个参数
// 2. 一个返回值
// 3. 功能为俩数之和
func mySum(a int, b int) int {return a + b
}
二、函数
根据上边内容,我们已经可以使用编写和调用函数。函数入参和出参,也可以有多种组合,比如有入参无出参,入参有多个出参也有多个… 当然这些场景都是根据具体的功能,进行选用,不做介绍!
1. 调用过程
编写一个main函数调用test函数的案例,并进行分析过程
package mainimport "fmt"func main() {num := 10test(num)fmt.Println("main num == ", num)//test num == 20//main num == 10
}func test(num int) {num = num + 10fmt.Println("test num == ", num)
}
在test函数,进行加10操作,过程如下所示
- 第一步:程序进入main函数,main会在栈空间进行创建空间,进行执行
- 第二步:调用test函数,test函数会进行压栈操作,进行放到栈的最上边,创建空间,并进行执行
- 第三步:释放test函数,test函数执行完毕,进行释放test空间,返回main函数
- 第四步:释放main函数,main函数执行完毕,进行释放main空间,结束程序
递归调用:上边的过程扩展开就是递归调用;一个函数调用另一个函数,另一个函数再进行调用其它函数,当受到某一个条件成立,进行栈顶释放空间,直到栈底!
2. 函数也是数据类型
还记得刚开始看到的Println()的代码吗?有个Println(a …any),其中any是个变量类型,但在学习go的变量中并没有学过,不属于数字、字符、布尔中的其中一个,那他是用来存储什么的???
查看源码,any是接口的别名,接口可以做变量!!!那函数呢???
尝试一下
package mainimport "fmt"func main() {fmt.Println("==main==")myFunc(printTest, 10)//==main==//==myFunc==//test num == 10
}type printTestType func(int)func myFunc(printTest printTestType, num int) {fmt.Println("==myFunc==")printTest(num)
}func printTest(num int) {fmt.Println("test num == ", num)
}
发现函数可以进行参数传递,可以运行,没有报错,猜想的没错!!!
3. 匿名函数
匿名函数,其实就是字面意思,没有名字的函数。
3.1 一次使用
// 匿名函数:俩数之和result := func(a int, b int) int {return a + b}(10, 20)fmt.Println("result = ", result)// result = 30
省略名称,后边直接跟上实参进行调用
3.2 多次使用
回马枪,其实还是变相的指定了一个名称
// 匿名函数:获取俩数之和 多次使用getSum := func(a int, b int) int {return a + b}result1 := getSum(10, 20)result2 := getSum(20, 30)fmt.Println("result1 = ", result1)fmt.Println("result2 = ", result2)// result1 = 30// result2 = 50
4. 闭包
什么是闭包:闭包就是一个函数和与其相关的引用环境组合的一个整体!
func addNum() func(int int) int {a := 10return func(x int) int {a = a + xreturn a}
}
addNum() 是一个函数,它返回另一个函数。
在 addNum() 内部,定义了一个局部变量 a,初始值为 10。
它返回一个匿名函数 func(x int) int,这个匿名函数可以访问并修改 addNum() 内部的变量 a。
这个匿名函数和它所依赖的变量 a 就构成了一个闭包。
addNumFunc := addNum()fmt.Println("addNum() = ", addNumFunc(10))fmt.Println("addNum() = ", addNumFunc(20))fmt.Println("addNum() = ", addNumFunc(30))//addNum() = 20//addNum() = 40//addNum() = 70
第一次调用 addNumFunc(10) 时,a 的值从 10 变成 20。
第二次调用 addNumFunc(20) 时,a 的值从 20 变成 40。
第三次调用 addNumFunc(30) 时,a 的值从 40 变成 70。
关键点:变量 a 的状态在多次调用之间被保持了。这就是闭包的“记忆”功能。
用生活中的例子来比喻
可以把闭包想象成一个带有“记忆”的盒子:
你创建了一个盒子(addNum()),里面放了一个初始值为 10 的计数器(a)。
每次你往盒子里放一个数字(调用返回的函数),它会把数字加到计数器上,并告诉你当前总计是多少。
盒子记住了上次的计数结果,不会每次重置。
闭包 = 函数 + 环境:闭包使得函数可以“记住”并访问其外部作用域的变量。
状态保持:即使外部函数执行完毕,闭包依然可以保持对局部变量的访问。
实用场景:常用于创建带状态的函数、工厂函数、回调函数等。
三、包
什么是包?换种说法,项目===公司,包===部门,包下的类===具体工作人员
!
也就是公司(项目)里的每一位员工(类),都会归属一个部门(包),所以包起到管理项目结构和文件的作用。
1. 包的作用
- 管理项目结构:当项目体量很大时,就需要用包的概念进行维护
- 区分相同的函数名、变量名等:一个项目中必然存在相同的变量的情况,就需要加上包进行区分
- 访问范围:限制函数、变量的访问范围
2. 包的使用
可以看下之前练习的前几行内容,熟悉的内容
// 包的申明
package main// 包的导入
import "fmt"
3. 包的尝试
根据上边描述的功能和使用,可以自己尝试下,进行调用,注意go.mod这个文件哈
编写MyTools.go文件
package utilimport "fmt"func MyTools() {fmt.Println("MyTools")
}
编写PackageTest.go文件
package mainimport "learn_golang_project/src/go_code/grade071/util"func main() {util.MyTools()
}
具体文档结构如下:
四、异常
啥是异常,生活是没有十全十美的,在编写代码的过程中,思考不全也是必经之路。那怎么提前预防呢???
1. 啥是异常
比如我想要俩个数相除,进行计算
package mainimport "fmt"func main() {num1 := 10num2 := 0fmt.Println("计算开始...")fmt.Println(num1 / num2)fmt.Println("计算结束...")
}
发现飘红了,而且我的程序也进行中断了,没有执行后边的语句!!!
当程序发生问题后,我想错误被捕获,并进行错误提示,如何解决呢???
2. 错误机制
go中使用:defer, panic, recover
啥意思呢?当程序抛出panic,然后在defer中通过recover进行捕捉这个异常
panic: bug怪兽
recover: 斩杀执行队
defer: 收尾/清扫队
大白话说:当一只bug怪兽出现,派出斩杀执行队进行斩杀,斩杀怪兽后进行收队,交接给收尾/清扫队,进行清理怪兽尸体和打扫卫生!!!
2.1 panic
刚刚进行计算抛出的错误,可以看到有个关键词panic,说明它是代表:错误
2.2 defer
收尾/清扫队,只要函数中加入了这个关键词,就会等函数内容执行完毕后,进行执行defer内容
package mainimport "fmt"func main() {defer func() {fmt.Println("我是收尾队...")}()fmt.Println("计算开始...")fmt.Println("计算结束...")
}
2.3 recover
进行斩杀队介入,recover进行接收函数最后的结果
package mainimport "fmt"func main() {defer func() {fmt.Println("recover==", recover())// recover== <nil>}()fmt.Println("计算开始...")fmt.Println("计算结束...")
}
那么可以实现之前,有错误进行捕捉,并进行提示的问题了!!!
package mainimport "fmt"func main() {defer func() {err := recover()if err != nil {fmt.Println("recover接收异常程序:", err)// recover接收异常程序: runtime error: integer divide by zero}}()num1 := 10num2 := 0fmt.Println("计算开始...")fmt.Println(num1 / num2)fmt.Println("计算结束...")
}
附
本次内容较多,但大都由函数延申过来
比如:我要100除以随机生成的0/1
函数:我们正常编写俩数相除就是函数的内容
包 :但发现随机数需要调用其它包下的功能,进行导入使用
异常:随机数有可能为零,会引发错误,这时我们需要解决错误
好了,以上内容到此结束!!!