Go语言中并没有类(class),所以并不是纯粹的面向对象语言。大多数都是用函数、结构体实现。
特性- go语言中有3种函数:普通函数、匿名函数(没有名称的函数)、方法(定义在struct上的函数)。
- go语言中不允许函数重载(overload),即:不允许函数同名。
- go语言中的函数不能嵌套函数,但可以嵌套匿名函数。
- 函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数。
- 函数可以作为参数传递给另一个函数。
- 函数的返回值可以是一个函数。
- 函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数。
- 函数参数可以没有名称。
func function_name([parameter list]) [return_types]{
函数体
}
-
func:函数由func开始声明
-
function_name:函数名称,函数名和参数列表一起构成了函数签名。
-
[parameter list]:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指
定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
-
return_types:返回类型,函数返回一列值。return_types是该列值的数据类型。(非必须可以不加)
-
函数体:函数定义的代码集合。
Demo01
addcompare
package main
import "fmt"
func add(a int, b int) (sum int) {
sum = a + b
return sum
}
func compare(x int, y int) (max int) {
if x > y {
max = x
} else {
max = y
}
return max
}
func main() {
sum := add(2, 3)
fmt.Println(sum)
max := compare(2, 3)
fmt.Println(max)
}
若没设置retrun的返回内容,则根据返回类型返回值
package main
import "fmt"
func test() (name string, age int) {
name = "Sentiment"
age = 18
return //相当于return name, age
}
func main() {
a, b := test()
fmt.Println(a, b)
}
结果:
Sentiment 18
变长参数
...type
Demo02
package main
import "fmt"
func add(nums ...int) (res int) {
for _, n := range nums {
res += n
}
return
}
func main() {
var result int
result = add(1, 2, 3)
fmt.Println(result)
}
//6
sliceslice...
package main
import "fmt"
func add(nums ...int) (res int) {
for _, n := range nums {
res += n
}
return
}
func main() {
var result int
slice := []int{7, 9, 3, 5, 1}
result = add(slice...)
fmt.Println(result)
}
//25
函数类型与函数变量
type
type f1 func( int, int) int
f1intint
f1f1f1
Demo03
package main
import "fmt"
type f1 func(int, int) int
func sum(a int, b int) int {
return a + b
}
func max(a int, b int) int {
if a > b {
return a
} else {
return b
}
}
func main() {
var f1 f1
f1 = sum //将sum函数赋值给f1类型的变量f1
s := f1(1, 2)
fmt.Println(s)
f1 = max //将max函数赋值给f1类型的变量f1
m := f1(3, 4)
fmt.Println(m)
}
结果:
3
4
将函数作为参数
其实就是函数回调。
package main
import "fmt"
func sayhello(name string) {
fmt.Printf("Hello,%s", name)
}
func test1(name string, f func(string2 string)) {
f(name)
}
func main() {
test1("Sentiment", sayhello)
}
匿名函数
所谓匿名函数就是没有名称的函数。
func(参数列表)(返回值)
Demo05
package main
import "fmt"
func main() {
max := func(a int, b int) int {
if a > b {
return a
} else {
return b
}
}
i := max(1, 2)
fmt.Println(i)
}
//2
或者也可以直接调用自己
package main
import "fmt"
func main() {
func(a int, b int) {
max := 0
if a > b {
max = a
} else {
max = b
}
fmt.Println(max)
}(1, 2)
}
//2
闭包
闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)
Demo06
package main
import "fmt"
func addupper() func(x int) int {
n := 10
return func(x int) int {
n += x
return n
}
}
func main() {
f := addupper()
fmt.Println(f(1)) //11
fmt.Println(f(2)) //13
fmt.Println(f(3)) //16
}
-
代码说明,addupper是一个函数,返回数据类型是func (x int) int
-
闭包部分:
n := 10
return func(x int) int {
n += x
return n
返回的是一个匿名函数,但是这个匿名函数引用到函数外的n,因此这个匿名函数和n形成了一个整体,构成闭包。
- 当我们反复调用f函数时,因为n是初始化一次,因此每次调用都进行累加
- 要搞清楚闭包的关键,就要分析出返回的函数它使用(引用)到哪些变量,因为函数和它引用到的变量共同构成闭包
闭包实践
请编写一个程序,具体要求如下:
- 编写一个函数makeSuffix(suffix string) 可以接收一个文件后缀名(如.jpg),并返回一个闭包
- 调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(如.jpg),则返回文件名.jpg,如果已经有.jpg后缀,则返回文件名
- 要求使用闭包的方式完成
package main
import (
"fmt"
"strings"
)
//- 编写一个函数makeSuffix(suffix string) 可以接收一个文件后缀名(如.jpg),并返回一个闭包
//- 调用闭包,可以传入一个文件名,如果该文件名没有指定的后缀(如.jpg),则返回文件名.jpg,如果已经有.jpg后缀,则返回文件名
//- 要求使用闭包的方式完成
func makeSuffix(suffix string) func(s string) string {
return func(s string) string {
if !strings.HasSuffix(s, suffix) {
return s + suffix
} else {
return s
}
}
}
func main() {
f := makeSuffix(".jpg")
x := f("Sentiment")
y := f("Sentiment.jpg")
fmt.Println(x)
fmt.Println(y)
}
结果:
Sentiment.jpg
Sentiment.jpg
也可以不用闭包实现,看下区别:
package main
import (
"fmt"
"strings"
)
func makeSuffix2(suffix string, s string) string {
if !strings.HasSuffix(s, suffix) {
return s + suffix
} else {
return s
}
}
func main() {
f1 := makeSuffix2(".jpg", "Sentiment") //设置后缀.jpg
f2 := makeSuffix2(".jpg", "Sentiment.jpg") //设置后缀.jpg
fmt.Println(f1)
fmt.Println(f2)
}
f := makeSuffix(".jpg")
递归
- 递归就是自己调用自己。
- 必须先定义函数的退出条件,没有退出条件,递归将成为死循环。
- go语言递归函数很可能会产生一大堆的goroutine,也很可能会出现栈空间内存溢出问题。
用个经典的斐波那契数列举例:
f(n)=f(n-1)+f(n-2)f(2)=f( 1)=1
package main
import "fmt"
func main() {
fmt.Println(fib(4))
}
func fib(n int) (res int) {
if n == 1 || n == 2 {
res = 1
} else {
res = fib(n-1) + fib(n-2)
}
return res
}
//3
defer
延迟deferdefer逆序deferdefer
特性
deferretrundeferdeferdefer
用途
- 关闭文件句柄
- 锁资源释放
- 数据库链接释放
Demo08
package main
import "fmt"
func main() {
fmt.Println("start")
defer fmt.Println("step1")
defer fmt.Println("step2")
defer fmt.Println("step3")
fmt.Println("end")
}
结果:
start
end
step3
step2
step1
ini函数
initmain
特点
- init函数先于main函数自动执行,不能被其他函数调用
- init函数没有输入参数,返回值
- 每个 可以有多个init函数
- 包的每个源文件也可以有多个init函数,这点比较特殊
- 同一个包的init执行顺序,golang没有明确定义,编程时要注意程序不用依赖这个执行顺序
- 不同包的init函数按照爆导入的依赖关系决定执行顺序
初始化顺序
顺序:变量初始化 -> init() -> main()
Demo09
package main
import "fmt"
var a int = initVar()
func init() {
fmt.Println("init2")
}
func init() {
fmt.Println("init")
}
func initVar() int {
fmt.Println("init var...")
return 100
}
func main() {
fmt.Println("main...")
}
结果:
init var...
init2
init
main...