1.参数,返回值
2.递归函数
3.回调函数
4.匿名函数
5.闭包
6.defer使用
7.变量作用域
8.timer,ticker,和异常处理
一、函数的定义
package main
import "fmt"
import "strconv"
//无参无返回值方法
func test_1() {
fmt.Println("调用test_1方法")
}
//有参无返回值方法
func test_2(s string) {
fmt.Println("调用test_2方法")
}
//无参有返回值方法
func test_3() string {
return "调用test_3方法"
}
//有参有返回值方法
func add(a, b int) int {
return a + b
}
//不定参数
func test_4(args ...int) {
for index, data := range args {
fmt.Printf("args 中第%d个数是:%d\n", index, data)
}
}
//取出不定参数中的第i个数
func test_5(i int, args ...int) string {
return "args 中第" + strconv.Itoa(i) + "个数是:" + strconv.Itoa(args[i])
}
func main() {
test_1()
test_2("")
fmt.Println(test_3())
fmt.Println("1 + 1 = ", add(1, 1))
test_4(1, 2, 3, 4, 5, 6)
fmt.Println(test_5(1, 2, 3, 4, 5, 6))
}
打印结果为:
调用test_1方法
调用test_2方法
调用test_3方法
1 + 1 = 2
args 中第0个数是:1
args 中第1个数是:2
args 中第2个数是:3
args 中第3个数是:4
args 中第4个数是:5
args 中第5个数是:6
args 中第1个数是:3
二、递归函数
递归函数就是自己调用自己本身的函数,注意点就是函数的出口。使用递归函数可以节省代码使程序看上去简洁,但是函数每次调用都要重新分配内存,内存占用较大,很可能会内存溢出。
使用递归函数计算0—99的和:
package main
import "fmt"
func test(num int) int {
num--
var result int
if num == 0 {
result += num
return result
} else {
result += test(num) + num
}
return result
}
func main() {
fmt.Println(test(100))
}
三、回调函数
一个函数作为另一个函数的参数,并在函数内部调用,这个函数就是回调函数。
package main
import "fmt"
func callback(s string) {
fmt.Println(s, "调用回调函数")
}
//函数可以作为参数传递,
func test(f func(string)) {
fmt.Println("调用test方法")
f("test")
}
func main() {
//callback为test方法的回调函数
//或者说是函数串联
test(callback)
}
四、匿名函数
省略函数名的函数
package main
import "fmt"
//函数可以作为参数传递,
func test(f func(string)) {
fmt.Println("调用test方法")
f("test")
}
func main() {
//此处使用匿名回调函数
test(func(s string) {
//函数在test函数中获取s值后再执行
fmt.Println(s, "调用匿名回调函数")
})
}
注意:1.匿名函数在程序中单独调用时需要在末尾加()才能被调用;
2.匿名函数可以赋值给变量,通过变量调用;
五、闭包
一个函数作为另一个函数的返回值,这个函数称为闭包函数
package main
import "fmt"
//函数返回值是一个匿名函数
func test() func() int {
var x int
//该返回的函数为闭包函数
return func() int {
x++
return x * x
}
}
func main() {
//将test()返回的闭包函数赋值给f
f := test()
//通过f()调用闭包函数
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
}
输出结果为1 4 9 16;
闭包函数的一个特性:不管函数中的变量是否超出了作用域,只要闭包函数还在使用,这些变量就还会存在。
六、defer的使用
defer关键字,后的语句会在所在函数调用完毕后执行,注意:不管函数是否报错,defer都会执行。
package main
import "fmt"
func test() {
fmt.Println("1")
//test函数调用完毕后打印“2”
defer fmt.Println("2")
fmt.Println("3")
}
func main() {
test()
fmt.Println("4")
//main函数调用完毕后打印“5”
defer fmt.Println("5")
fmt.Println("6")
}
程序的执行结果为1 3 2 4 6 5.因为defer的特性,可以用来关闭输入输出流等。
七、作用域
局部变量,在函数体内部声明的变量,或是循环体内声明的变量,局部变量的生命周期依赖于它所在的函数体或循环体。
全局变量,是在go文件中与main函数同级声明的变量,它的生命周期与主程序相同。
局部变量的作用域是它所在的函数或循环体内,全局变量的作用域是在整个程序中。
八、timer,ticker,和异常处理
1.timer是一个延时器函数,使程序延时一段时间后执行。ticker是一个定时器,执行一次后,ticker会继续阻塞。
timer和ticker实际上是一个channel,go语言通过通信实现同步,
package main
import "fmt"
import "time"
func main() {
//NewTimer创建一个Timer,它会在规定时间段后到期,向其自身的C字段发送当时的时间。
timer := time.NewTimer(time.Second * 2)
for {
//C字段中没有数据会一直阻塞
t := <-timer.C
fmt.Println(t)
}
}
程序在2s后打印当前时间,然后报错:fatal error: all goroutines are asleep - deadlock!
因为timer在执行完一次之后不会再向C中写数据,程序会一直阻塞在 <-timer.C 所以报死锁。
而将上面程序中的timer换做ticker:
package main
import "fmt"
import "time"
func main() {
ticker := time.NewTicker(time.Second * 2)
for {
t := <-ticker.C
fmt.Println(t)
}
}
程序会每隔2s打印一次当前时间。
2.go语言异常处理,首先创建一个error:
package main
import (
"errors"
"fmt"
)
func main() {
err := errors.New("使用字符串创建一个错误,返回error")
fmt.Println(err)
}
error主要作为函数返回值使用,捕获函数执行中的错误。
panic函数,程序出现致命性错误是调用panic函数,使程序崩溃
package main
import "fmt"
func main() {
fmt.Println("1")
//显示调用panic函数使程序中断
panic("this is panic")
fmt.Println("1")
}
程序在打印一次1后遇到panic函数,导致程序中断,无法打印第二个1.
recover函数,程序在奔溃后保护程序继续执行,recover函数会返回程序崩溃的错误信息,并且recover函数只能放在defer后的函数中使用;
package main
import "fmt"
func test() {
defer func() {
fmt.Println(recover())
}()
panic("this is panic")
}
func main() {
fmt.Println("1")
test()
fmt.Println("1")
}
test函数中显示调用panic函数导致程序中断,test调用完毕后执行defer后的匿名函数,recover又将程序恢复并打印panic函数的错误,所以打印结果为:
1
this is panic
1