语法格式:
func 函数名(参数)(返回值) {
函数体
}
,
分隔。x int, y int
可以写成x, y int
。...
来标识可变参数。可变参数在函数中通过切片来实现。可变参数必须放在固定参数的后面。示例如下:func sum2(x ...int) int {
fmt.Println(x) // [1 2 3 4 5]
sum := 0
for _, v := range x {
sum += v
}
return sum
}
func main() {
sum := sum2(1, 2, 3, 4, 5)
fmt.Println(sum) // 15
}
Go语言通过return
关键字输出返回值,且支持多返回值。
单返回值
func test1() int {
return 3
} // 调用时直接返回3
多返回值
func test2() (int, string) {
return 3, "多返回值"
}
多返回值的接收方式:
a, b := test2()
fmt.Println(a, b) // 3 多返回值
上面的a
和b
是自定义的变量名,用来接收返回值,不想接收某个值时可以用_
代替变量名。
返回值有命名时可以直接用return
返回,当然写上变量名也没有关系。
命名的返回值可以直接使用,不需要再声明。
命名的多返回值如果类型相同,也可以类型简写。
func test3() (a int, b string) {
a = 3
b = "有命名"
return
}
调用后得到结果如下:
a, b := test3()
fmt.Println(a, b) // 3 有命名
Go语言中函数也是一种类型,也就是说可以声明函数类型的变量,并为其赋值。
示例:
func add(x, y int) int {
return x + y
}
func main() {
f1 := add // 将函数add赋值给变量f1
fmt.Printf("%T\n", f1) // func(int, int) int
fmt.Println(f1(1, 2)) // 3
}
还可以使用type
关键字定义一个函数类型,格式如下:
type f func(int, int) int
这定义了一个f
类型,是一种函数类型。
接上例:
var f2 f // 声明一个f类型的变量f2
f2 = add // 这里add的类型必须与f类型相同,包括参数和返回值
fmt.Printf("%T\n", f2) // main.f
fmt.Println(f2(1, 2)) // 3
高阶函数,将函数作为参数或者返回值。
// f2的参数op是函数类型
func f2(x, y int, op func(int, int) int) int {
return op(x, y)
}
func main() {
fmt.Println(f2(2, 3, add)) // 5
}
// 调用f4会返回add函数
func f4() func(int, int) int {
return add
}
func main() {
fmt.Println(f4()(4, 5)) // 9
}
匿名函数就是没有名字的函数。在函数内部只能定义匿名函数。匿名函数没有函数名,不能通过函数名调用,但可以作为返回值返回、可以保存到变量,通过变量来调用、还可以作为立即执行函数。
示例:
// 匿名函数作为返回值
func f6() func(int, int) int {
return func(x, y int) int {
return x * y
}
}
func main() {
// 保存到变量
f5 := func(x, y int) int {
return x + y
}
fmt.Println(f5(3, 5)) // 8
fmt.Println(f6()(2, 3)) // 6
func(x, y int) { // 立即执行函数
fmt.Println(x - y) // 3
}(4, 1)
}
闭包是一个函数,这个函数包含了它外部作用域的变量。
闭包简单的、通用的定义是指:函数引用一个词法变量,在函数或语句块结束后(变量的名称消失),词法变量仍然对引用它的函数有效。示例如下:
func f(x int) func(int) int {
return func(y int) int { // 返回一个闭包
x = x + y
return x
}
}
func main() {
f1 := f(1) // f1是一个闭包,它引用了函数f中的变量x,在f在函数返回后退出,x这个名称也随之消失,但是x的值仍旧存在,直到f1被消除
fmt.Println(f1(2)) // 3
fmt.Println(f1(3)) // 6
f2 := f(3) // f2也是一个闭包,但是它引用的变量x和f1是相互独立的,这是闭包的特性
fmt.Println(f2(2)) // 5
fmt.Println(f2(3)) // 8
}
闭包更严格的定义:闭包是一种在支持一级函的编程语言中能够将词法作用域中的变量名称进行绑定的技术。在操作上,闭包是一种用于保存函数和环境的记录。这个环境记录了一些关联性的映射,将函数的每个自由变量与创建闭包时所绑定名称的值或引用相关联。通过闭包,就算是在作用域外部调用函数,也允许函数通过闭包拷贝他们的值或通过引用的方式去访问那些已经被捕获的变量。
总之,要牢记闭包
=函数
+引用环境
。
close
:主要用于关闭channel
len
:求长度,如map
、slice
、string
、array
、channel
new
:分配值类型的内存,如int
、struct
,返回对应类型的指针(用法)make
:分配引用类型的内存,如map
、slice
、channel
,返回类型本身(用法)append
:slice
、array
追加元素(用法)panic/recover
:错误处理panic/recover
Go语言使用panic/recover
机制来处理错误。程序出错时会引发panic
,导致程序直接异常退出,这会造成占用的资源无法释放(比如打开的文件)。对应的recover
能解决这个问题,使程序恢复运行。
注意:
recover
必须搭配defer
使用defer
一定要在引发panic
之前定义func f1() {
fmt.Println("第一步")
}
func f2() {
panic("第二步出错")
}
func f3() {
fmt.Println("第三步")
}
func main() {
f1()
f2()
f3()
}
输出:
第一步
panic: 第二步出错
goroutine 1 [running]:
main.f2(...)
e:/go/src/github.com/BattleL/studygo/test/main.go:17
main.main()
e:/go/src/github.com/BattleL/studygo/test/main.go:25 +0x9d
exit status 2
可以看到引发panic
后程序直接退出,不会再往下运行。
修改函数f2
,加入recover
:
func f2() {
// defer放在触发panic的语句之前
defer func() {
err := recover() // 如果程序没有出现panic,返回nil
if err != nil {
fmt.Println("继续运行")
}
}()
panic("第二步出错")
}
输出变为:
第一步
继续运行
第三步
可以看到程序继续往下运行了。
整体参考、闭包参考