一、包
go的每一个文件都是属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构的。
包的作用
- 区分相同名字的函数、变量等标识符
- 当程序文件较多,可很好的管理项目
- 控制函数、变量等访问范围,即作用域
二、函数细节
- 函数本身也是一种数据类型,可以赋值给一个变量,该变量就是一个函数类型的变量,通过该变量可以对函数调用
func getSum(n1 int, n2 int) int {
return n1 + n2
}
func myFunc(funcvar func(int, int) int, num1 int,num2 int) {
return funcvar(num1, num2)
}
func main() {
res := myFunc(getSum, 2, 4)
fmt.Println("a的类型是%T, getSum 的类型是%T", a, getSum)
}
- 为了简化数据类型定义,go支持自定义数据类型
type myInt int
var num1 myInt
var num2 int
num1 = 40
num2 = int(num1) // myInt 和int是两个类型,所以要强转
- 支持对函数返回值命名
func getSumAndSub(n1 int, n2 int) (sum int, sub int) {
sum = n1 + n2
sub = n1 - n2
return // 好处是不用管返回的顺序,接收按顺序接收就行
}
三、init函数
每一个源文件都可以有一个init函数,该函数会在main函数执行前,被Go框架调用。如果一个文件包含全局变量定义,init函数和main函数,则执行流程是 变量定义>init函数>main函数,如果main.go 引入了包,则执行流程是 包的变量定义>包的init函数>main的变量定义>main的init函数>main的main函数
func init() {
fmt.Println("init()...")
}
func main() {
fmt.Println("main()...")
}
三、匿名函数
如果希望函数只使用一次,可以考虑匿名函数,匿名函数也可实现多次调用。
var (
// 全局匿名函数
Func1 := func (n1 int, n2 int) int {
return n1 + n2
}
)
func main() {
// 在定义匿名函数时直接调用, 这种方式匿名函数只能调用一次
result := func (n1 int, n2 int) int {
return n1 + n2
}(n1, n2)
// 将匿名函数func (n1 int, n2 int) int 赋值给a变量,则a的数据类型就是函数类型,此时可通过a进行调用
a := func (n1 int, n2 int) int {
return n1 + n2
}
res := a(10, 20)
fmt.Println("main()...")
}
三、闭包
一个函数和其相关的引用环境组合的一个整体(实体)
func AddUpper() func (int) int {
var n int = 10;
return func (x int) int {
n = n + x
return n
}
}
func main() {
f := AddUpper()
fmt.Println(f(1)) // 11
fmt.Println(f(2)) // 13
}
上面代码说明:
返回的是一个匿名函数,但这个匿名函数引用到函数外的n,因此这个匿名函数就和n形成一个整体,构成闭包
- 可以理解闭包是一个类,函数是操作,n是字段,函数和它使用到的n构成闭包
- 当我们反复调用f函数时,因为n只初始化一次,因此每调用一次就进行累计
- 要搞清楚闭包的关键,就是要分析出返回的函数它使用(引用)到哪些变量,因为函数和它引用到的变量共同构成闭包
四、函数中的defer
在函数中,经常要创建资源(数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时释放资源。Go的设计者提供defer(延时机制)
func sum (n1 int, n2 int) int {
// 当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈(defer栈)
// 当函数执行完毕后,再从defer栈,按照先入后出的方式出栈,执行
defer fmt.Println("n1 = ", n1)
defer fmt.Println("n2 = ", n2)
return n1 + n2
}
func main() {
res := sum(10, 20)
fmt.Printf("%T, %T", f, a) // 151
}
五、函数的参数传递
- 值类型:默认值传递,变量直接存储值,内存通常在栈中分配
- 引用类型:默认引用传递,变量存储的是一个地址,这个地址对应的空间才真正存储数据,内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾由GC回收
六、错误处理
Go中引入处理方式是defer,panic,recover
Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理
func test () {
defer func() {
err := recover() // 内置函数,可以捕获到异常
if err != nil { // 捕获到错误
fmt.Println("err=", err)
}
}()
num1 := 10
num2 := 0
res := num1 / num2
fmt.Println("res", res)
}
func main() {
test()
fmt.Printf("%T, %T", f, a) // 151
}