函数定义格式 # func 函数名(参数列表)(返回值列表){
函数体
}
1.无参且无返回值
func 方法名(){
}
2.有参且无返回值
func 方法名(参数列表且多个逗号分隔){
}
3.有参且有返回值
func 方法名(参数列表且多个逗号分隔) (返回值类型列表且多个逗号分隔){
}
func 方法名(参数名1,参数名2 参数类型, 参数名3 参数类型) 返回值类型{
}
4.可变参数
func 方法名1(v ...可变参数类型) (返回参数列表){
//可变参数,说明参数个数不固定,在可变参数类型前加...(即3个点)
//把可变参数原样传递给其它方法时,只要在可变参数名后加...
方法名2(v...)
}
func 方法名2(固定参数列衰, v ...可变参数类型) (返回参数列表){
//可变参数,说明参数个数不固定,在可变参数类型前加...(即3个点)
//把可变参数原样传递给其它方法时,只要在可变参数名后加...
方法名2(v...)
}
5.匿名函数:
func(参数列表)(返回值){
函数体
}
备注:方法名又叫函数名
方法名可由字母、数字、下划线组成,且不能以数字开头
在同一包内方法名不能重复(即不能重名,方法名不能相同,因为go方法不支持重载。重写)
参数:参数由参数变量名和参数变量的类型组成,多个参数之间使用,分隔。
返回值:返回值由返回值变量名和其变量类型组成,也可以只写返回值的类型,多个返回值必须用()包裹,并用逗号,分隔。
函数体:实现指定功能的代码块。
不支持默认值参数(即参数没有默认值)
不支持函数重载
不支持有名函数嵌套定义,但支持匿名函数嵌套在内
函数定义示例 # //多个参数和多返回值示例,相邻参数/变量类型相同则类型可省略
func test1(a,b int) (int, int){//等价 func test1(a int,b int) (int, int){
return a+b, a*b
}
a1,b1 := test1(5, 6)
//返回值带有变量的示例
func test2(a,b int) (q, r int){
q = a+b
r = a*b
return // 自动对号入座返回相应变量
}
q, r := test2(1,2)
//返回错误对象
func test3(a,b int)(int, error) {
if a+b>100{
return a+b, fmt.Errorf("%s","error!")
}else{
return a+b, nil
}
}
sum,err:=test3(2,3)
函数变量 # 函数变量: 就是把方法赋值给变量
var 变量名 func() //声明变量存无参无返回值函数
变量名 := 方法名 //给函数类型变量赋值
var 变量名 func() 返回值类型
var 变量名 func() (返回值类型列表,多个逗号分隔) //声明变量存无参有返回值函数
var 变量名 func(参数类型列表) 返回值类型
var 变量名 func(参数类型列表) (返回值类型列表) //声明变量存有参有返回值函数
调用变量函数: 变量名()、变量名(参数列表)
类型相同的变量才可以直接赋值
判断函数类型相同:形参列表相同和返回值列表也要相同(即列表元素的次序、个数和类型均相同)
函数类型变量的默认值为nil
var doFun func(int, int) (int, int)
fmt.Printf("%v \n", doFun) //<nil>
示例1:
package main
import "fmt"
func main() {
_getName01 := getName01 //变量函数
var _getName02 func(string) string
_getName02 = getName01
fmt.Println(_getName01("tom")) //hi,tom
fmt.Println(_getName02("jelly")) //hi,jelly
}
func getName01(name string) string {
return "hi," + name
}
示例2:
package main
import "fmt"
func main() {
var a,b int = 8, 3
var doFun func(int, int) (int, int) //定义函数类型变量
doFun = valDemo //给函数类型变量赋值
s1,s2:=doFun(a, b) //调用函数
fmt.Println(s1, s2) //11 5
fmt.Printf("%T \n", valDemo) //func(int, int) (int, int)
}
func valDemo(a,b int) (int, int) {
sum := a+b
sub := a-b
return sum,sub
}
函数类型与变量 # 1. 定义函数类型,
格式:type 类型名 func(参数) 返回值类型列表
示例:type abcFunc func(int, int) int
2.
func add(x, y int) int {//相加
return x + y
}
func sub(x, y int) int {//相减
return x - y
}
var c abcFunc //定义变量类型
c = add //给变量赋值函数名
调用:
fmt.Printf("type of c:%T\n", c) //type of c:main.abcFunc
fmt.Println(c(1, 2)) //3
匿名函数 # 匿名函数: 没有方法名的函数、又叫闭包
匿名函数用途:可以赋值给变量、也可以作为方法参数传入、也可以作为方法返回值返回
匿名函数定义:
func(参数列表)(返回参数列表){
函数体
}
闭包自执行(匿名函数自执行):
func(参数列表)(返回参数列表){
函数体
}(参数列表)
匿名函数赋值给变量:
变量名 := func(参数列表)(返回参数列表){
函数体
}
匿名函数赋值给变量示例:
myF := func() string{// 将匿名函数保存到变量
return "my name is jelly"
}
//调用
fmt.Println(myF()) //my name is jelly
匿名函数用途:多用于实现回调函数和闭包
匿名函数作为参数传递示例 # 示例1-接收匿名函数:
package main
import "fmt"
func main() {
myNum := []int{1, 3,4,8,10}
//匿名函数作为参数传入
myPrint(myNum, func(i int) {
fmt.Println("总值为" ,i) //总值为 26
})
}
//参数接收方法,参数的定义:变量名 方法类型
func myPrint(num []int, fn func(int)) int {
var total = 0
for _, i := range num { //求和
total += i;
}
//回调
fn(total)
return total
}
示例2-返回匿名函数:
func adder2(x int) func(int) int {
return func(y int) int {
x += y
return x
}
}
func main() {
var f = adder2(10)
fmt.Println(f(10)) //20
fmt.Println(f(20)) //40
fmt.Println(f(30)) //70
f1 := adder2(20)
fmt.Println(f1(40)) //60
fmt.Println(f1(50)) //110
}
示例3-参数接收函数:
func add(x, y int) int {
return x + y
}
func calc(x, y int, op func(int, int) int) int {
return op(x, y)
}
func main() {
ret2 := calc(10, 20, add)
fmt.Println(ret2) //30
}
示例4-自动加后缀:
func makeSuffixFunc(suffix string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}
func main() {
jpgFunc := makeSuffixFunc(".jpg")
txtFunc := makeSuffixFunc(".txt")
fmt.Println(jpgFunc("test")) //test.jpg
fmt.Println(txtFunc("test")) //test.txt
}
方法返回匿名函数示例 # package main
import "fmt"
func main() {
myF01 := myFunc01(2)
fmt.Println(myF01()) //3
fmt.Println(myF01()) //4
myF02 := myFunc01(100)
fmt.Println(myF02()) //101
fmt.Println(myF02()) //102
}
//函数返回闭包(即匿名函数),这个闭包的用途:计数
func myFunc01(num int) func() int {
//返回闭包
return func() int {
num++ //每执行一次均对参数num变量+1
return num
}
}
函数作为返回值 # func add(x, y int) int {
return x + y
}
func sub(x, y int) int {
return x - y
}
func do(s string) (func(int, int) int, error) {
switch s {
case "+":
return add, nil
case "-":
return sub, nil
default:
err := errors.New("无法识别的操作符")
return nil, err
}
}
func main() {
ret2,err := do("+")
if(err== nil) {
fmt.Println(ret2(1, 8)) //9
} else {
fmt.Println("不支持的方法," + err.Error())
}
}
可变参数示例 # package main
import "fmt"
func main() {
val01 := myFunc01(2,3,4)
fmt.Println(val01) //9
var a []int = []int{1,4,7} //定义切片
fmt.Println(myFunc02(a...)) //12
}
//可变参数
func myFunc01(num ...int) int {
total := myFunc02(num...) //可变参数值传递给其它方法
return total
}
func myFunc02(n ...int) int {
t :=0
for _,v:= range n{
t +=v
}
return t
}
函数中访问全局变量函数示例 # package main
import "fmt"
//定义全局变量num
var num int64 = 10
func printNum01() {
//证明:函数中可以访问全局变量num
fmt.Printf("num=%d\n", num)
}
func main() {
printNum01() //打印:num=10
}
备注:如果局部变量和全局变量重名,优先访问局部变量
package main
import "fmt"
//定义全局变量num
var num int64 = 10
func printNum02() {
num := 100 //局部变量与全局变量重名
fmt.Printf("num=%d\n", num) // 函数中优先使用局部变量
}
func main() {
printNum02() // num=100
}
按值传递与引用传递 # 引用传递: 切片(slice)、map(字典)、接口(interface)、通道(信道 channel)、指针类型
按值传递: 数组、int系列(数值型)、string、bool、float系列(浮点值)、结构体
示例1-切片引用传递:
package main
import (
"fmt"
)
func main() {
var xyz map[string]string = map[string]string{"abc":"123", "x":"x1"}
hi(xyz)
fmt.Println(xyz) //map[abc:hello x:x1 appendc:c12]
fmt.Println(xyz["appendc"]) //c12
}
func hi(a map[string]string) {
a["abc"] = "hello"
a["appendc"] = "c12"
}
示例2-切片引用传递:
package main
import (
"fmt"
)
func main() {
a := []int{2, 3}
b := a //切片变量赋值为引用赋值
fmt.Println(a,b) //[2 3] [2 3]
b[1] = 77
fmt.Println(a,b) //[2 77] [2 77]
}
示例3-切片引用传递:
package main
import (
"fmt"
)
func main() {
a := []int{2, 3}
edit(a) //切片引用传递
fmt.Println(a) //[2 99]
}
func edit(a []int) {//参数接收切片
a[1] = 99
}
示例4-数组按值传递:
a := [2]int{2, 3}
b := a //数组按值传递
fmt.Println(a,b) //[2 3] [2 3]
b[1] = 8
fmt.Println(a,b)//[2 3] [2 8]
示例5-指针引用传递:
package main
import (
"fmt"
)
func main() {
a := 8
say(&a) //指针引用传递
fmt.Println(a) //99
}
func say(a *int) {
*a = 99
}
main函数和init函数 # Go有两个保留的函数分别是main()和init()方法
main(): 入口函数
init():包初始化函数
相同点:
1. Go程序会自动调用init()和main()方法,即不需要在任何地方主动调用这两个函数
2. 均无参无返回值,自动调用,即无法主动调用初始化函数和main()函数
差异性:
1. main()方法在package main包中定义与使用,作为程序的入口函数
2. 每个包中均可定义一个或多个init()方法,但强烈建议开发者在一个package中每个文件只写一个init函数
main函数引入包初始化流程图如下:
main函数引入包初始化流程图 实参与形参的传递示例 # 按值传递、引用传递
package main
import "fmt"
func main() {
var a int = 4
pointerDemo(&a)
fmt.Println(a) //9
valDemo(a)
fmt.Println(a) //9
fmt.Printf("%T \n", pointerDemo) //func(*int)
fmt.Printf("%T \n", valDemo) //func(int)
}
//指针参数引用传递
func pointerDemo(a *int) {
*a = *a + 5
}
//普通变量按值传递
func valDemo(a int) {
a = a+1
}
go内置全局函数 # func len(v Type) int 用来获取长度,比如字符串string、数组array、切片slice、map、信道channel等类型值的长度
func cap(v Type) int 获取容量大小
func print(args ...Type) 打印1个或多个变量值
func println(args ...Type) 打印1个或多个变量值,并换行
func complex(r, i FloatType) ComplexType
func real(c ComplexType) FloatType 取复数的实部数
func imag(c ComplexType) FloatType 取复数的虚部数
func panic(v interface{}) 抛出错误,主动发起恐慌
func recover() interface{} 捕获恐慌,解决宕机
func new(Type) *Type 返回指针
func make(t Type, size ...IntegerType) Type 初始化切片/map/chan变量,返回切片、map,通道类型
func delete(m map[Type]Type1, key Type) 删除map的key
func append(slice []Type, elems ...Type) []Type 向切片追加元素
func copy(dst, src []Type) int 复制切片
func close(c chan<- Type) 关闭通道