基本语法
Go中函数的基本语法如下:
使用案例如下:
package main import "fmt" // 函数定义 func Factorial(num int) int { result := 1 for i := 2; i <= num; i++ { result *= i } return result } func main() { result := Factorial(10) // 函数调用 fmt.Printf("result = %d\n", result) // result = 3628800 }
注意: Go中的函数不支持函数重载。
参数传递方式
参数传递的方式有两种:
值传递案例如下:
package main import "fmt" func ModifyNum(num int) { // 值传递 num = 20 fmt.Printf("num = %d\n", num) // num = 20 } func main() { var num int = 10 ModifyNum(num) fmt.Printf("num = %d\n", num) // num = 10(原始数据未被影响) }
对于值类型参数,如果希望函数内部对参数的修改能影响到原始数据,可以传入值类型变量的地址,然后在函数内部通过指针的方式操作变量。如下:
package main import "fmt" func ModifyNum(num *int) { // 引用传递 *num = 20 fmt.Printf("num = %d\n", *num) // num = 20 } func main() { var num int = 10 ModifyNum(&num) fmt.Printf("num = %d\n", num) // num = 20(原始数据被影响) }
返回多个值
Go中函数支持返回多个值,通过返回值列表指明各个返回值的类型即可。如下:
package main import "fmt" func GetSumAndSub(num1 int, num2 int) (int, int) { // 返回多个值 sum := num1 + num2 sub := num1 - num2 return sum, sub } func main() { sum, sub := GetSumAndSub(10, 20) fmt.Printf("sum = %d\n", sum) // sum = 30 fmt.Printf("sub = %d\n", sub) // sub = -10 }
说明一下:
忽略返回值
如果函数返回多个值,在接收时,可以通过_(占位符)忽略不需要的返回值。如下:
package main import "fmt" func GetSumAndSub(num1 int, num2 int) (int, int) { // 返回多个值 sum := num1 + num2 sub := num1 - num2 return sum, sub } func main() { _, sub := GetSumAndSub(10, 20) fmt.Printf("sub = %d\n", sub) // sub = -10 }
返回值命名
Go中函数支持在返回值列表给返回值命名,这时函数在返回时无需在return后指明需要返回的值,可以避免返回顺序出错。如下:
func GetSumAndSub(num1 int, num2 int) (sum int, sub int) { // 返回多个值 sum = num1 + num2 sub = num1 - num2 return }
可变参数
Go中函数支持可变参数,只需在对应形参类型的前面加上...即可将其设置为可变参数,可变参数支持传入0个或多个参数。如下:
package main import "fmt" func Sum(nums ...int) (sum int) { // 可变参数 fmt.Printf("nums type = %T\n", nums) // nums type = []int for i := 0; i < len(nums); i++ { sum += nums[i] } return } func main() { fmt.Printf("result1 = %d\n", Sum()) // result1 = 0 fmt.Printf("result2 = %d\n", Sum(10, 20)) // result2 = 30 fmt.Printf("result3 = %d\n", Sum(10, 20, 30)) // result3 = 60 }
说明一下:
函数类型
在Go中函数也是一种数据类型,可以将其赋值给一个变量,然后通过该变量即可对函数进行调用。如下:
package main import "fmt" func Sum(num1 int, num2 int) int { return num1 + num2 } func main() { sumFunc := Sum fmt.Printf("sumFunc type = %T\n", sumFunc) // sumFunc type = func(int, int) int fmt.Printf("sum = %d\n", sumFunc(10, 20)) // sum = 30 }
自定义数据类型
Go中自定义数据类型的基本语法如下:
使用案例如下:
package main import "fmt" func main() { type MyInt int // 自定义数据类型 var num1 MyInt = 10 fmt.Printf("num1 type = %T\n", num1) // num1 type = main.MyInt var num2 int = int(num1) fmt.Printf("num2 type = %T\n", num2) // num2 type = int }
说明一下:
在Go中将函数作为形参也是常见的用法,这时结合自定义数据类型给函数类型取别名,能有效提高代码的可读性。如下:
package main import "fmt" type SumType func(int, int) int // 自定义数据类型 func MyFunc(f SumType, num1 int, num2 int) int { return f(num1, num2) } func main() { result := MyFunc(Sum, 10, 20) fmt.Printf("result = %d\n", result) // result = 30 }
匿名函数
Go中支持匿名函数,如果希望某个函数只使用一次,可以考虑使用匿名函数。如下:
package main import "fmt" func main() { // 匿名函数 result := func(num1 int, num2 int) int { // 定义匿名函数并调用 return num1 + num2 }(10, 20) fmt.Printf("result = %d\n", result) // result = 30 }
在定义匿名函数时如果将其赋值给一个变量,后续再通过该变量来调用匿名函数,就能实现匿名函数的多次调用。如下:
package main import "fmt" func main() { // 匿名函数 f := func(num1 int, num2 int) int { // 定义匿名函数 return num1 + num2 } fmt.Printf("result = %d\n", f(100, 200)) // result = 300 }
defer机制
defer相关介绍:
下面是一个经典的defer题目,请问程序的输出结果是什么。如下:
package main import "fmt" func DeferFunc(num1 int, num2 int) { defer fmt.Printf("defer num1 = %d\n", num1) defer fmt.Printf("defer num2 = %d\n", num2) num1++ num2++ fmt.Printf("num1 = %d\n", num1) fmt.Printf("num2 = %d\n", num2) } func main() { DeferFunc(10, 20) fmt.Println("main code...") }
程序运行结果如下:
说明一下:
defer使用案例
下面是一个对文件操作的函数,其中利用defer机制对文件资源进行了延时释放,在函数执行完毕后文件会自动关闭。如下:
func FileOperation(filename string) (err error) { file, err := os.Open(filename) // 打开文件 if err != nil { fmt.Printf("open file error, err = %v\n", err) return } defer file.Close() // 关闭文件 // 进行文件操作... return }
闭包
闭包相关介绍:
使用案例如下:
package main import "fmt" // 创建累加器 func CreateCounter(base int) func(int) int { var count = base return func(n int) int { // 返回闭包 count += n return count } } func main() { // 闭包 f := CreateCounter(10) fmt.Printf("count = %d\n", f(1)) // count = 11 fmt.Printf("count = %d\n", f(3)) // count = 14 fmt.Printf("count = %d\n", f(5)) // count = 19 }
说明一下:
闭包的优势
闭包的优势如下:
基本概念
在Go语言中,包是一种组织和封装代码的方式,每一个go文件都归属于一个包,而一个包可以包含多个文件。
包的主要优势如下:
在Go项目中,通常一个包对应一个目录,包名与目录名相同,每个目录下的go文件都归属于当前包。如下:
包的使用
包的使用方式如下:
例如,下面在Go项目中创建了db、utils、main三个目录,分别用于存放db包、utils包和main包的go文件代码,并在main包中调用了db包和utils包中的函数。如下:
说明一下:
init函数
在每一个go文件中都可以包含一个init函数,该函数会在main函数执行前被Go运行框架调用。如下:
package main import ( "fmt" ) func init() { fmt.Println("main init...") } func main() { fmt.Println("main run...") }
代码的运行结果如下:
说明一下:
init函数的执行顺序
init函数的执行顺序如下:
下面分别在之前的main包、utils包和db包中添加了一些输出代码,方便看到程序的执行顺序。
main.go文件中的代码如下:
package main import ( "fmt" "go_code/FunctionAndPackage/PackageDemo/db" "go_code/FunctionAndPackage/PackageDemo/utils" ) var globalNum = test() func init() { fmt.Println("main init...") } func test() int { fmt.Println("main global variable init...") return 0 } func main() { fmt.Println("main run...") db.DbPrint() utils.UtilsPrint() }
utils.go文件中的代码如下:
package utils import "fmt" var globalNum = test() func init() { fmt.Println("utils pakcage init...") } func test() int { fmt.Println("utils global variable init...") return 0 } func UtilsPrint() { fmt.Println("utils package print...") }
db.go文件中的代码如下:
package db import "fmt" var globalNum = test() func init() { fmt.Println("db pakcage init...") } func test() int { fmt.Println("db global variable init...") return 0 } func DbPrint() { fmt.Println("db package print...") }
运行程序后可以看到,按照main包中的导入顺序先后对db包和utils包进行了初始化,然后再对main包进行了初始化,每个包在初始化过程中先对全局变量进行了初始化,然后再调用了init函数,在所有包初始化完毕后开始执行main函数的代码逻辑。如下:
说明一下: