我正在参加「掘金·启航计划」
1、函数声明
- 使用关键字
func定义函数,左大括号不能另起一行。 - 函数声明包含一个函数名,参数列表,返回值列表和函数体。如果函数没有返回值,则返回列表可以省略。
- 函数从第一条语句开始执行,直到执行return语句或者执行函数的的最后一条语句。
- 函数可以可以没有参数或接受多个参数。
- 当两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略。
- 函数可以返回任意数量的返回值。
- 函数名首字母大写为公有函数,小写为私有函数
func FuncName(x, y int, s string) (in, string){
//类型相同的相邻参数,参数类型可合并。多返回值必须用括号
sum := x + y
return sum, fmt.Sprintf(s, sum)
}
函数是第一类对象,可作为参数传递。
有返回值的函数,必须有明确的终止语句,否则会引发编译错误。
2、参数
- 形参:函数定义时传递的参数,形参就像定义在函数体内的局部变量。
- 实参:函数调用时传递过来的变量。
函数可以通过两种方式来传递参数,默认情况下是使用的值传递,无论是值传递还是引用传递,传递给函数的都是变量的副本,值传递的是值得拷贝,引用传递是地址的拷贝。
- 值传递:在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数
- 引用传递:在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
注意:map、chan、slice、指针、interface默认以引用的方式传递。
不定参数传值就是函数的参数不是固定的,后面的类型是固定的,可变参数本质上是slice,只能有一个,且必须是最后一个。
在参数赋值时可以不用一个一个的赋值,可以直接传递一个数组或者切片,在参数后面加上...就可以。
func add(args ...int){//0个或多个参数
}
//调用
s:=[]int{1,2,3}
add(s...) //slice 展开slice
args是一个slice,我们可以通过args[index]依次访问所有参数,通过len(args)来判断传递参数的个数。s
3、返回值
_标识符,用来忽略函数的某个返回值。
4、匿名函数
匿名函数是由一个不带函数名的函数声明和函数体组成,在Go里面,函数可以像普通变量一样被传递或使用。
匿名函数的优点是可以直接使用函数内得变量,不必声明。
func main(){
fn := func(a int)int{
return a+a
}
fmt.Println(fn(3))
}
5、闭包
闭包是由函数和其相关的引用环境组合而成的实体(即 闭包=函数+引用环境)。
简单描述:
- 函数体内嵌套另一个函数,并且返回值是函数体。
- 主函数体内的子函数被主题函数外变量引用时,就创建一个闭包。
- 不同的变量引用各自调用没有关系。
- 闭包函数不会被GC回收所占用资源,因此会出现外部调用时会接着上次调用的结果。
func a() func() int{
i := 0
b:= func() int(){
i++
fmt.Println(i)
return i
}
return b
}
func main(){
c := a()
c()
c()
c()
c()
a() //不会输出i
}
输出结果
1
2
3
4
6、递归
递归就是在运行的过程中调用自己。
构成递归需具备的条件:
- 子问题须与原始问题为同样的事,且更为简单。
- 不能无限制地调用本身,须有个出口,化简为非递归状况处理。
举例:数字阶乘
func jiecheng(x int) int{
if x <= 1 {
return 1
}
return x * jiecheng(x-1)
}
func main(){
v := jiecheng(5)
fmt.Println(v) //输出120
}
7、延迟调用(defer)
7.1、defer特性
- 关键字defer用于注册延迟调用
- 这些调用直到return前才被执行,因此可以用来做资源清理
- 多个defer语句,按先进后出的方式执行。
- defer语句中的变量,在defer声明时就决定了。
7.2、defer用途:
- 关闭文件句柄
- 锁资源释放
- 数据库连接释放
func main(){
for i :=0; i < 5; i++ {
defer fmt.Println(i)
}
}
//输出结果 4 3 2 1 0
defer碰上闭包:
函数正常执行,由于闭包用到的变量i在执行的时候已经变成5, 所以输出全部都是5。
func main(){
for i :=0; i < 5; i++ {
defer func(){fmt.Println(i)}()
}
}
//输出 5 5 5 5 5
8、异常处理
golang没有结构化异常,使用panic抛出错误,recover捕获错误。Go中可以抛出一个panic的异常,然后再defer中通过recover捕获这个异常,然后正常处理。
panic:
- 内置函数;
- 假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行;
- 返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行;
- 直到goroutine整个退出,并报告错误;
recover:
- 内置函数
- 用来控制一个goroutine的panicking行为,捕获panic,从而影响应用的行为
- 一般的调用建议:
-
- 在defer函数中,通过recover来终止一个goroutine的panicking过程,从而恢复正常代码的执行
- 可以捕获通过panic传递的error
注意:
- 利用recover处理panic指令,defer必须放在panic之前定义,另外recover只有在defer调用的函数中才有效。否则当panic时,recover无法捕获到panic,无法防止panic扩散。
- recover处理异常后,逻辑并不会恢复到panic那个点去,函数跑到defer之后的那个点。
- 多个defer会行程defer栈,后定义的defer语句会被最先调用。
func main(){
test()
}
func test(){
defer func(){
if err := recover(); err !=nil {
println(err.(string))
}
}
panic("test error")
}
//结果输出: test error