笔记整理全部整理于文档库页面 点我前往

Golang中每一个文件都是属于一个包的,也就是说Golang是以包的形式管理文件和项目结构的

1
2
3
4
5
6
7
8
9
package myPackage //打包

import "myPackage" //引包

import{
newPackage "myPackage" //给包起别名
"fmt"
//引入多个包
}

函数

函数的递归

1
2
3
4
5
6
7
func recursion(n int){
if n > 2{
n--
recursion(n)
}
rmt.Println("n=", n)
}

函数&变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func test(num int) int {
num++ //改变的是函数内的num值
return num
}

func testPointer (numPointer *int) int {
*num ++
return *num
//通过指针的方式改变函数外的变量 通过传入变量的地址来操作变量的值
}

func main(){
var num int = 20
var numPointer int = 30
test(num)
testPointer(&numPointer)
a := test(33) //将函数赋值给一个变量并调用

//传入变量的地址
fmt.Println("num = ", num) //20 //函数外的变量不受影响
fmt.Println("numPointer = ", numPointer) //31 //通过指针修改了函数外的变量
}

函数作为形参

回调函数

这两段选自我当时学习的时候,给我无限启发的两位大佬的文章,我也粘贴过来,供大家参考

回调函数这4个字容易让人理解为调用别人,这是错误的,如果在前面加上一个”被”字就好理解了,即等着被中间函数调用,只不过回调函数是主程序写的,也就是自己的意思,所以叫回调。

我是这么记忆的:我把我写的函数,传递给你,希望你在某个时候 回过头来调用 我的这个函数。那么这个函数就称为回调函数

  • 有一家旅馆提供叫醒服务,但是要求旅客自己决定叫醒的方法。可以是打客房电话,也可以是派服务员去敲门,睡得死怕耽误事的,还可以要求往自己头上浇盆水。这里,“叫醒”这个行为是旅馆提供的,相当于中间函数,但是叫醒的方式是由旅客决定并告诉旅馆的,也就是回调函数。而旅客告诉旅馆怎么叫醒自己的动作,也就是把回调函数传入中间函数的动作,称为登记回调函数(to register a callback function)
  • 你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里, 你的电话号码就叫 *回调函数* ,你把电话留给店员就叫 *登记回调函数* ,店里后来有货了叫做 *触发了回调关联的事件* ,店员给你打电话叫做 *调用回调函数* ,你到店里去取货叫做 *响应回调事件*
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import "fmt"

//回调函数
//生成一个2k形式的偶数
func Double(x int) int {
return x * 2
}

//中间函数
//接受一个生成偶数的函数作为参数
//返回一个奇数
func getOddNumber(k int, getEvenNumber func(int) int) int {
return 1 + getEvenNumber(k)
}

//起始函数,这里是程序的主函数
func main() {
k := 1

//当需要生成一个2k+1形式的奇数时
i := getOddNumber(k, Double)
fmt.Println(i)
}

闭包

也算是函数作为形参的一个常见用途,闭包是一个函数和与其相关的引用环境组合的一个整体,一般用于减少变量的作用域,减少对于全局变量的污染。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func add() func (int) int{
var int = 233
return func (x int) int {
n = n + x
return n
}
}
func main(){
f := add()
// 把这个函数变量赋值给f,f成为了一个闭包,f保存着对n的引用,f中有一个指针指向x,
// 由于这个指针的存在,所以可以随时修改x并保持这个状态
// 在这个过程中,n变量逃逸了,他的生命周期并没有随着作用域结束而结束
fmt.Println(f(1)) // 234
fmt.Println(f(2)) // 236
// 这里调用了3次add(),返回三个闭包,三个闭包引用三个不同的n值,他们的状态是各自独立的,所以n的数值不会发生变化
fmt.Println(add()) //233
fmt.Println(add()) //233
}

函数の可变参数

重要:如果一个函数有多个形参,那么可变函数要放在形参列表的最后。

1
2
3
4
5
6
7
8
9
10
func addSum(num int, args... int) sum int{
sun := n1
for i := 0; i < len(args); i++ {
sum += args[i]
}
return sum
}
func main(){
fmt.Println(sum(1, 2, 3, 4, 5))
}

init函数

每个包和每个源文件都可以包含一个或多个init函数,该函数会在main函数之前被调用,用于初始化不能采用初始化表达式初始化的变量,如果一个包同时包含全局定义,init函数,main函数,则执行顺序为 全局变量定义 -> init函数 -> main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// myPackage.go

//① 首先执行 myPackage 包的全局变量定义
var Name string
var Age int

//② 执行 myPackage 包内的init函数
func init(){
fmt.Println("init -> MyPackage.go")
Name = "Tom"
Age = 18
}
// Main.go

import "myPackage"

//④ 执行 Main 包的init函数
func init(){
fmt.Println("init -> main.go")
}

//③ 执行 Main 包的变量定义
var age = test()

func test() int {
return 233
}

//⑤ 最后执行 Main 包的Main函数
func main(){
fmt.Println("age =", age)
fmt.Println("Age = ", myPackage.Age, "Name = ", myPackage.Name)
}

匿名函数

匿名函数多用于模拟块级作用域,避免数据污染

1
2
3
4
5
6
func main() {
f:=func(args string){
return args + "helloworld"
}
fmt.Println(f("Tony, "))
}

函数的 Defer

一般用于资源的释放,Defer用于提供延时,当执行到defer的时候,不会立即执行,会先将defer后语句压入栈中(存在值拷贝),然后继续执行,在函数执行完毕之后,依次从栈顶取出,遵循先入后出机制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func add(num1 int, num2 int) int {
//入栈的时候存在值拷贝 所以 此时 num1 = 1 num2 = 2
defer fmt.Println("0x1 num1 = ", num1)
defer fmt.Println("0x2 num2 = ", num2)

num1++
num2++

fmt.Println(" 0x3 num1 = ", num1, "num2 = ", num2)
return num1 + num2
}
// 所以 最终输出为:0x3 num1 = 2 num2 = 3 / 0x2 num = 2 / 0x1 num1 = 1 / 5
func main(){
result := add(1, 2)
fmt.Println(result)
}

函数的参数传递

函数中的参数传递分为值传递和引用传递,不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值的

拷贝,引用传递的是地址的拷贝,一般来说,地址拷贝效率高,因为数据量小,而值拷贝决定拷贝的 数据大小,数据越大,效率越低。