一、函数
fun 方法名称(形参列表) 返回值列表 {
}
二、包
包的基本概念
go的每一个文件都是属于包的,也就是说go是以包的形式来管理文件和项目目录结构。
包的作用
1)区分相同名字的函数、变量等表示符。
2)当程序文件很多时,可以很好的管理项目
3)控制函数、变量等访问范围,即作用域
包的打包和引入
打包基本语法
package “包名”
引入包的基本语法
import “包的路径”
注意:包中的方法名称开头字母要大写,否则即使引入包也访问不了方法,因为小写的方法名称表示“私有方法”。
第一步:创建一个文件,并写函数。在utils文件夹下创建一个utils.go文件(包),并编写 Cal方法。
package utils
import (
"fmt"
)
//为了让其他包的文件使用Cal函数,需要将"C"大写,类似于其他语言的“public”
func Cal(n1 float64,n2 float64,operator byte) float64{
var res float64
switch operator {
case '+':
res = n1 + n2
case '-':
res = n1 - n2
case '*':
res = n1 * n2
case '/':
res = n1 / n2
default:
fmt.Println("操作符号错误...")
}
return res
}
第二步:main.go文件中引入包
注意:默认引入路径是从"src"开始,即"src"是可以不用写
package main
import (
"fmt"
"go_code/fordemo/utils"
)
第三步:调用方法
注意:调用格式为:包名.方法名(参数)
package main
import (
"fmt"
"go_code/chapter04/fordemo/utils"
)
func main(){
var n1 float64 = 1.2
var n2 float64 = 2.3
var operator byte = '+'
result := utils.Cal(n1,n2,operator)
fmt.Println("result = ",result)
}
三、init函数(初始化)
基本介绍
每一个源文件都可以包含一个init函数,该函数会在main函数执行前,被go运行框架调用,也就是说init会在main函数前被调用。
package main
import (
"fmt"
)
//init函数,通常可以在init函数中完成初始化工作
func init() {
fmt.Println("init()...")
}
func main(){
fmt.Println("main()...")
}
init的注意事项和细节
1)如果一个文件同时包含全局变量定义,init函数和main函数,则执行的流程是全局变量定义->init函数->mian函数
package main
import (
"fmt"
)
//定义一个全局变量
var age =test()
//第一个执行
//此函数用于测试 全局变量age的执行顺序
func test() int {
fmt.Println("test()")
return 90
}
//第二个执行
//init函数,通常可以在init函数中完成初始化工作
func init() {
fmt.Println("init()...")
}
//第三个执行
func main() {
fmt.Println("main()...age = ",age)
}
2)init函数最主要的作用,就是完成一些初始化的工作
四、匿名函数
介绍
Go支持匿名函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数,匿名函数也可以实现多次调用。
匿名函数使用方式1(项目开发中常用)
在定义匿名函数时就直接调用
package main
import (
"fmt"
)
func main() {
//在定义匿名函数时就直接调用
res1 := func(n1 int, n2 int) int {
return n1 + n2
}(10, 20)
fmt.Println("res1 = ", res1)
}
匿名函数使用方式2
将匿名函数赋给一个变量(函数变量),在通过该变量来调用匿名函数
package main
import (
"fmt"
)
func main() {
//将匿名函数func(n1 int,n2 int) int 赋给a变量
//则a的数据类型就是函数类型,此时,我们可以通过a完成调用
a := func(n1 int,n2 int) int {
return n1 - n2
}
rest2 := a(10,30)
fmt.Println("res2 = ",rest2)
rest3 := a(90,30)
fmt.Println("res3 = ",rest3)
}
全局匿名函数
如果将匿名函数赋给一个全局变量,那么这个匿名函数,就成为一个全局函数,可以在程序有效。
package main
import (
"fmt"
)
var (
//fun1 就是一个全局匿名函数
Fun1 = func (n1 int,n2 int) int {
return n1 * n2
}
)
func main() {
//全局匿名函数的使用
res4 := Fun1(4,9)
fmt.Println("rest4 = ",res4)
}
五、闭包
介绍
闭包就是一个函数和与其关联的引用环境组合的一个整体(实体)
package main
import (
"fmt"
)
//累加器
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
fmt.Println(f(3)) //结果是 16
}
对上面代码的说明和总结
1)AddUpper 是一个函数,返回的数据类型是 fun(int) int
2) 闭包的说明
返回的是一个匿名函数,但是这个匿名函数引用到函数外的n,因此这个匿名函数就和n行成一个整体,构成闭包。
3)可以这样理解:闭包是一个类,函数是操作,n是字段。函数和它使用到的n构成一个闭包。
4)当我们反复调用f函数时,因此n是初始化一次,因此每调用一次就进行累计。
5)闭包的关键就是要分析出返回的函数它使用到哪些变量,因为函数和它引用到的变量共同构成闭包。
六、defer语句
在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,go的设计者提供defer(延迟机制)。
defer介绍
package main
import (
"fmt"
)
func sum(n1 int, n2 int) int {
//当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈(defer栈)
//当函数执行完毕后,在从defer栈,按照先入后出的方式出栈执行。
defer fmt.Println("ok1 n1 = ", n1) //3.ok1 n1 = 10
defer fmt.Println("ok2 n2 = ", n2) //2.ok2 n2 = 20
res := n1 + n2
fmt.Println("ok3 res = ", res) // 1. ok3 = 30
return res
}
func main() {
res := sum(10, 20)
fmt.Println("res = ", res) //4.res = 30
}
defer的细节说明
1)当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈(暂且叫做defer栈)中,然后继续执行函数下一个语句。
2)当函数执行完毕后,在从defer栈中,依次从栈顶取出语句执行。
3)在defer将语句放入到栈时,也会将相关的值拷贝同时入栈。
package main
import (
"fmt"
)
func sum(n1 int, n2 int) int {
//当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈(defer栈)
//当函数执行完毕后,在从defer栈,按照先入后出的方式出栈执行。
defer fmt.Println("ok1 n1 = ", n1) //3.ok1 n1 = 10(将值也放入栈中)
defer fmt.Println("ok2 n2 = ", n2) //2.ok2 n2 = 20
res := n1 + n2 //30
n1++ //11
fmt.Println("ok3 res = ", res,"n1 = ",n1) // 1. ok3 res = 30 n1 = 11
return res
}
func main() {
res := sum(10, 20)
fmt.Println("res = ", res) //4.res = 30
}
defer的最佳实践
defer最主要的价值在,当函数执行完毕后,可以及时的释放函数创建的资源。