公眾號(hào)模板免費(fèi)關(guān)鍵詞優(yōu)化技巧
GO系列
1、GO學(xué)習(xí)之Hello World
2、GO學(xué)習(xí)之入門語法
3、GO學(xué)習(xí)之切片操作
4、GO學(xué)習(xí)之 Map 操作
5、GO學(xué)習(xí)之 結(jié)構(gòu)體 操作
6、GO學(xué)習(xí)之 通道(Channel)
7、GO學(xué)習(xí)之 多線程(goroutine)
8、GO學(xué)習(xí)之 函數(shù)(Function)
9、GO學(xué)習(xí)之 接口(Interface)
文章目錄
- GO系列
- 前言
- 一、什么是函數(shù)?
- 二、函數(shù)聲明
- 三、函數(shù)調(diào)用
- 四、匿名函數(shù)
- 五、函數(shù)參數(shù)和返回值
- 六、延遲執(zhí)行函數(shù)
- 6.1 defer 先進(jìn)后出
- 6.2 defer 閉包函數(shù)
- 七、錯(cuò)誤處理
- 7.1 使用 error 作為返回參數(shù)
- 7.2 使用 panic 觸發(fā)異常
- 八、總結(jié)
前言
按照公司目前的任務(wù),go 學(xué)習(xí)是必經(jīng)之路了,雖然行業(yè)卷,不過技多不壓身,依舊努力!!!
之前在 結(jié)構(gòu)體 篇中提到了方法,那方法其實(shí)和函數(shù)是差不多的,不過方法與函數(shù)的區(qū)別是,函數(shù)不屬于任何類型,方法屬于特定的類型,這句話在 結(jié)構(gòu)體 篇中也說到了,但函數(shù)的使用上也有許多細(xì)節(jié)需要注意的,此篇?jiǎng)t給予詳解。
一、什么是函數(shù)?
- 在 Go 語言中,函數(shù)(Function)是一種可執(zhí)行的代碼塊(對(duì)特定功能進(jìn)行提取,形成代碼片段),用于執(zhí)行特定的任務(wù)或操作。
- 函數(shù)是 Go 語言中的基本組件,實(shí)現(xiàn)了模塊化和復(fù)用的機(jī)制,讓代碼更加結(jié)構(gòu)化和可維護(hù)。
下面列舉了我能查到和想到的特點(diǎn)(不僅限于這些):
Go語言中函數(shù)的特點(diǎn):
- 無需聲明原型
- 支持不定參數(shù)
- 支持多返回值
- 支持命名返回參數(shù)
- 支持匿名函數(shù)和閉包
- 函數(shù)也是一種類型,可以把一個(gè)函數(shù)賦值給一個(gè)變量
- 不能嵌套定義函數(shù)
- 不能像JAVA中那樣重載函數(shù)(overload)
二、函數(shù)聲明
- 函數(shù)的聲明使用 關(guān)鍵字
func
- 基本語法:
func 函數(shù)名(參數(shù)列表) 返回值列表 { 函數(shù)體 }
- 函數(shù)名命名規(guī)范:1、最好駝峰命名,見名知意,比如:
addNum(a,b int){}
;2、首字母不能是數(shù)字;3、首字母大寫表示可以被本包和其他包文件使用,類似 public,比如:AddNum(a,b int){}
,首字母小寫則類似 private,比如:addNum(a,b int){}
- 參數(shù)列表用逗號(hào)分隔,每個(gè)參數(shù)有參數(shù)名和類型組成,比如:
func list(pageNo int, pageSize int) (int, error) { }
- 如果多個(gè)參數(shù)是同一類型,則前面參數(shù)類型可以省略,比如:
func list(pageNo, pageSize int) (int, error) { }
- 使用
...
語法可以為函數(shù)定義可變參數(shù),運(yùn)行函數(shù)接受不定數(shù)量參數(shù),比如:`` - 如果是無放回值則可省略,比如:
func save(id int, name string) { }
- 如果是一個(gè)返回值則不需要(或有都可)小括號(hào),比如:
func save(id int, name string) int { }
- 如果是多個(gè)返回值則用小括號(hào)包起來,并且和 return 語句一 一對(duì)應(yīng),比如:
func save(id int, name string) (int, string) { ...return 1,'success' }
- 上面提到支持命名返回函數(shù),比如:
func divideAndRemainder(a, b int) (quotient, remainder int) { }
- 使用關(guān)鍵詞
func
定義函數(shù),大括號(hào)不能另起一行
上代碼:
三、函數(shù)調(diào)用
- 在接受函數(shù)返回值時(shí),如果多個(gè)返回值則一一對(duì)應(yīng)接受,比如:
count, result := save(1, "張三")
- 如果只需要接受其中一個(gè)返回值,另一個(gè)不需要接受,則可以用下劃線
_
忽略,比如:count, _ := save(1, "張三")
- 如果 main 包中想調(diào)用其他包中的函數(shù),那其他包中的函數(shù)則需要定義為包外可訪問的,函數(shù)名首字母大寫,比如定義一個(gè) func1 的包:
func SumNum(a, b int) int { }
,此時(shí)需要注意此包名,必須使用包名調(diào)用,比如:s := func1.SumNum(1, 2)
下面例子,舉例了 Go 語言常用到的函數(shù)定義的例子,可供參考。
例子是哥我一個(gè)一個(gè)敲的,測(cè)試通過,并且附上了運(yùn)行結(jié)果,不過只靠眼睛看還是有點(diǎn)繁雜,還是自己在專門敲一遍為好,不過對(duì)于大佬無所謂了,對(duì)于像我這種小白,則只能按部就班敲一遍,解決各種報(bào)錯(cuò),方能成長!
包路徑是這樣的:
package func1import "fmt"// 定義無參數(shù)無返回值函數(shù)
func test() {fmt.Println("call test函數(shù)")
}// 定義有參數(shù)無返回值函數(shù),此函數(shù)私有的,只有內(nèi)部可調(diào)
func addNum(a, b int) {c := a + bfmt.Printf("a + b = c %+v\n", c)
}// 定義有參數(shù)有一個(gè)返回值函數(shù), 次函數(shù)共有的,內(nèi)部、外部包均可調(diào)
func SumNum(a, b int) int {c := a + breturn c
}// 定義可變參數(shù)函數(shù)
func ParamsFunc(params ...string) {for index, item := range params {fmt.Printf("可變參數(shù)為 %d:%+v\n", index, item)}
}// 定義有參數(shù)有多個(gè)返回值函數(shù)
func List(pageNo, pageSize int) (int, []string) {fmt.Printf("查詢操作...%d, %d", pageNo, pageSize)result := []string{"特斯拉", "廣汽", "豐田", "寶馬", "奧迪"}return 5, result
}// 定義命名返回函數(shù)
func divideAndRemainder(a, b int) (quotient, remainder int) {quotient = a / bremainder = a % breturn // 省略了 return 語句,并且直接返回了命名的返回值變量
}
下面示例是對(duì)上面定義的函數(shù)進(jìn)行調(diào)用。
主要,import 其他包的路徑是 gotest.com/test/src/functionTest/func1
package mainimport ("fmt""gotest.com/test/src/functionTest/func1"
)func main() {// 調(diào)用本包中的 save 函數(shù),接受兩個(gè)返回值count1, result := save(1, "張三")fmt.Printf("接受 save 函數(shù)的兩個(gè)返回值 count1:%+v, result: %v\n", count1, result)// 調(diào)用本包中的 save 函數(shù),接受一個(gè)返回值count, _ := save(1, "張三")fmt.Printf("接受 save 函數(shù)的一個(gè)返回值 count: %+v\n", count)// 調(diào)用無返回值函數(shù)list2(1, 10)// 調(diào)用 func1 包中的 SumNum 函數(shù)s := func1.SumNum(1, 2)fmt.Printf("調(diào)用 func1 包中的 SunNum 函數(shù)結(jié)果:%+v\n", s)// 調(diào)用可變參數(shù)函數(shù)func1.ParamsFunc("特斯拉", "廣汽", "豐田", "寶馬", "奧迪")// 調(diào)用 func1 包中的 List 函數(shù)totalCount, carBrands := func1.List(1, 10)fmt.Printf("調(diào)用 func1 包中的 List 函數(shù),查詢結(jié)果:%+v 條,數(shù)據(jù):%v\n", totalCount, carBrands)
}// 定義有參數(shù)有多個(gè)返回值函數(shù)
func save(id int, name string) (int, string) {fmt.Printf("保存%+v,%v\n", id, name)return 1, "success"
}// 定義有多個(gè)參數(shù)無返回值函數(shù)
func list2(pageNo, pageSize int) {fmt.Println("list 接口")
}
運(yùn)行結(jié)果:
PS D:\workspaceGo\src\functionTest\main> go run funcTest.go
保存1,張三
接受 save 函數(shù)的兩個(gè)返回值 count1:1, result: success
保存1,張三
接受 save 函數(shù)的一個(gè)返回值 count: 1
list 接口
調(diào)用 func1 包中的 SunNum 函數(shù)結(jié)果:3
可變參數(shù)為 0:特斯拉
可變參數(shù)為 1:廣汽
可變參數(shù)為 2:豐田
可變參數(shù)為 3:寶馬
可變參數(shù)為 4:奧迪
查詢操作...1, 10調(diào)用 func1 包中的 List 函數(shù),查詢結(jié)果:5 條,數(shù)據(jù):[特斯拉 廣汽 豐田 寶馬 奧迪]
四、匿名函數(shù)
- 在 Go 語言中,支持匿名函數(shù),也就是沒有函數(shù)名的函數(shù)
- 可以將匿名函數(shù)賦值給變量
- 也可以將匿名函數(shù)直接調(diào)用,則是閉包
package mainimport "fmt"func main() {// 定義匿名函數(shù)直接調(diào)用func() {fmt.Println("匿名函數(shù)調(diào)用!")}()// 定義匿名函數(shù)賦值給變量 hellohello := func() {fmt.Println("Hello 函數(shù)調(diào)用!")}// 調(diào)用匿名函數(shù)hello()// 定義有參數(shù)的匿名函數(shù)sum := func(a, b int) int {return a + b}fmt.Printf("加法計(jì)算:%+v\n", sum(1, 2))
}
運(yùn)行結(jié)果:
PS D:\workspaceGo\src\functionTest\main> go run .\anonymousFunc.go
匿名函數(shù)調(diào)用!
Hello 函數(shù)調(diào)用!
加法計(jì)算:3
下面是一個(gè)稍微復(fù)雜點(diǎn)的例子:
下面例子中,我們把 函數(shù) 作為一個(gè)成員存放在了 數(shù)組 fns、結(jié)構(gòu)體 s、管道 fc 中,并且獲取到函數(shù)進(jìn)行調(diào)用。
package mainfunc main() {// 定義數(shù)據(jù),元素類型是一個(gè)函數(shù)fns := [](func(a int) int){func(a int) int { return a + 1 }, func(a int) int { return a + 2 }}// 獲取數(shù)組中的第一個(gè)函數(shù)調(diào)用,傳參 10for _, fn := range fns {println(fn(10))}// 定義一個(gè)結(jié)構(gòu)體,成員是一個(gè) 函數(shù),調(diào)用結(jié)構(gòu)體的 函數(shù)成員s := struct {fn func() string}{fn: func() string { return "Hello World!" },}println(s.fn())// 定義一個(gè)管道,發(fā)送一個(gè)函數(shù),再接受到函數(shù)進(jìn)行調(diào)用fc := make(chan func() string, 2)fc <- func() string { return "fc: Hello World!" }println((<-fc)())
}
運(yùn)行結(jié)果:
PS D:\workspaceGo\src\functionTest\main> go run .\anomymousFunc2.go
11
12
Hello World!
fc: Hello World!
五、函數(shù)參數(shù)和返回值
- 在 Go 語言中,函數(shù)可以作為參數(shù)傳遞,也可以作為另一個(gè)函數(shù)的返回值
下面是一個(gè)比較簡(jiǎn)單的示例,例子中接受一個(gè) 函數(shù)類型參數(shù) fc,返回一個(gè) 匿名函數(shù)。
package func1import "fmt"func CallFunc(fc func()) func() {fmt.Println("接受到函數(shù) fc, 開始回調(diào)!")// 返回一個(gè)匿名函數(shù)return func() {fc()fmt.Println("call back...")}
}
調(diào)用代碼:
package mainimport ("fmt""gotest.com/test/src/functionTest/func1"
)func main() {
fc := func() {fmt.Println("我是參數(shù) fc 執(zhí)行!")}fr := func1.CallFunc(fc)fr()
}
運(yùn)行結(jié)果:
PS D:\workspaceGo\src\functionTest\main> go run .\funcTest.go
接受到函數(shù) fc, 開始回調(diào)!
我是參數(shù) fc 執(zhí)行!
call back...
下面是 ChatGPT給出的經(jīng)典案例,方便更加深入理解函數(shù)如何作為參數(shù)和返回值在實(shí)際場(chǎng)景中的應(yīng)用,示例我已測(cè)試,ojbk。
- 函數(shù)作為參數(shù)使用:
package mainimport "fmt"// 函數(shù)類型作為參數(shù)
type MathFunc func(int, int) int// 加法函數(shù)
func add(a, b int) int {return a + b
}// 減法函數(shù)
func subtract(a, b int) int {return a - b
}// 計(jì)算函數(shù),接收一個(gè)函數(shù)類型參數(shù),并執(zhí)行該函數(shù)
func calculate(a, b int, op MathFunc) int {return op(a, b)
}func main() {// 調(diào)用 calculate 函數(shù),傳入 add 函數(shù)作為參數(shù)result := calculate(10, 5, add)fmt.Println("加法結(jié)果:", result)// 調(diào)用 calculate 函數(shù),傳入 subtract 函數(shù)作為參數(shù)result = calculate(10, 5, subtract)fmt.Println("減法結(jié)果:", result)
}
運(yùn)行結(jié)果:
PS D:\workspaceGo\src\functionTest\main> go run .\gptFunc.go
加法結(jié)果: 15
減法結(jié)果: 5
- 函數(shù)作為返回值使用:
package mainimport "fmt"// 返回一個(gè)加法函數(shù)
func getAddFunc() func(int, int) int {// 返回一個(gè)匿名函數(shù),來實(shí)現(xiàn)計(jì)算return func(a, b int) int {return a + b}
}// 返回一個(gè)減法函數(shù)
func getSubtractFunc() func(int, int) int {return func(a, b int) int {return a - b}
}func main() {// 獲取加法函數(shù)并調(diào)用addFunc := getAddFunc()result := addFunc(10, 5)fmt.Println("加法結(jié)果:", result)// 獲取減法函數(shù)并調(diào)用subtractFunc := getSubtractFunc()result = subtractFunc(10, 5)fmt.Println("減法結(jié)果:", result)
}
執(zhí)行結(jié)果:
PS D:\workspaceGo\src\functionTest\main> go run .\gptFunc2.go
加法結(jié)果: 15
減法結(jié)果: 5
六、延遲執(zhí)行函數(shù)
defer 的特性:
- 關(guān)鍵字 defer 用戶注冊(cè)延遲調(diào)用,比如:
defer println(i)
- 注冊(cè)的延遲調(diào)用直到 return 前才會(huì)執(zhí)行,所以很適合做關(guān)閉、資源回收等操作
- defer 語句,是按照先進(jìn)后出的方式執(zhí)行
- defer 語句中的變量,在 defer 聲明是就決定了
defer 適用場(chǎng)景:
- 關(guān)閉流操作
- 資源釋放
- 數(shù)據(jù)庫連接釋放
- 等…
6.1 defer 先進(jìn)后出
package mainfunc main() {arr := [5]int{1, 2, 3, 4, 5}for i := range arr {defer println(i)}
}
運(yùn)行結(jié)果:
PS D:\workspaceGo\src\functionTest\main> go run .\deferTest.go
4
3
2
1
0
從結(jié)果可以看出,先循環(huán)到的 defer 等到后面才執(zhí)行。
6.2 defer 閉包函數(shù)
package mainfunc main() {arr := [5]int{1, 2, 3, 4, 5}for i := range arr {defer func() {println(i)}()}
}
運(yùn)行結(jié)果:
PS D:\workspaceGo\src\functionTest\main> go run .\deferTest.go
4
4
4
4
4
為啥全部變?yōu)榱?4,由于循環(huán)體內(nèi)的是閉包函數(shù),聲明完之后立馬執(zhí)行,但是在函數(shù)聲明的時(shí)候 i 變量已經(jīng)變?yōu)榱?4,所以 4 個(gè)匿名函數(shù)都輸出了 4。
由于 defer 看起來情況比較多,所以請(qǐng)移步到這里!
七、錯(cuò)誤處理
- Go 語言中的多數(shù)函數(shù)會(huì)返回一個(gè)錯(cuò)誤
error
作為額外的返回值,用戶表示函數(shù)是否執(zhí)行成功 - 調(diào)用函數(shù)通常需要檢查錯(cuò)誤,以便根據(jù)情況進(jìn)行處理
- 對(duì)于無返回值的函數(shù),可以使用錯(cuò)誤類型來表明函數(shù)是否執(zhí)行成功,或者用
panic
來觸發(fā)異常
7.1 使用 error 作為返回參數(shù)
在示例中,我們使用錯(cuò)誤類型 error 來表示函數(shù)是否執(zhí)行成功,如果函數(shù)出現(xiàn)錯(cuò)誤,則返回 error。
package mainimport ("errors""fmt"
)func main() {err := divide(10, 0)if err != nil {fmt.Println("發(fā)生異常:", err)}
}func divide(a, b int) error {if b == 0 {return errors.New("參數(shù)不能為 0")}return nil
}
運(yùn)行結(jié)果:
PS D:\workspaceGo\src\functionTest\main> go run .\errorFunc.go
發(fā)生異常: 參數(shù)不能為 0
7.2 使用 panic 觸發(fā)異常
示例中,用 panic 來觸發(fā)異常表示函數(shù)執(zhí)行狀態(tài),當(dāng)函數(shù)出現(xiàn)錯(cuò)誤時(shí),直接觸發(fā)異常,并中斷程序執(zhí)行。
**注意:**我們用 recover() 函數(shù)來捕獲并處理異常,避免程序崩潰。recover
函數(shù)只在defer
塊中才有效,所以在 main() 函數(shù)中使用 defer 來捕獲異常。
package mainfunc main() {defer func() {if r := recover(); r != nil {println("發(fā)生異常:", r)}}()divide2(10, 0)
}func divide2(a, b int) {if b == 0 {panic("參數(shù)不能為 0")}
}
運(yùn)行結(jié)果:
PS D:\workspaceGo\src\functionTest\main> go run .\panicFunc.go
發(fā)生異常: (0xff1920,0x1011638)
八、總結(jié)
函數(shù)是 Go 語言中非常重要的組成部分,它們提供了模塊化、代碼復(fù)用和抽象的能力。通過函數(shù),我們可以將復(fù)雜的邏輯劃分為多個(gè)小模塊,使得代碼更加清晰、可讀性更強(qiáng),并且更易于維護(hù)和擴(kuò)展。函數(shù)的靈活性和多樣性使得 Go 語言可以用于解決各種不同的問題和場(chǎng)景。
現(xiàn)階段還是對(duì) Go 語言的學(xué)習(xí)階段,想必有一些地方考慮的不全面,本文示例全部是親自手敲代碼并且執(zhí)行通過。
如有問題,還請(qǐng)指教。
評(píng)論去告訴我哦!!!一起學(xué)習(xí)一起進(jìn)步!!!