[TOC]
>[success]## **一、 函數介紹**
>[info]### 1.1 定義
函數:有輸入、有輸出,用來執行一個指定任務的代碼塊。
~~~
func functionname([parametername type]) [return type] {
//function body
} //其中參數列表和返回值列表都是可選的
~~~
解釋:
~~~
func 函數名([參數名 類型])[返回值 類型] {
函數體
}
~~~
<br>
<br>
>[info]### **1.2 特點**
golang函數的特點:
1)不支持重載,即一個包不能有兩個名字一樣的函數
2)函數也是一種類型,一個函數可以賦值給變量
3)匿名函數
4)多返回值
>[info]### **1.3 return函數**
:-: 
<br>
>[]#### **多返回值**
返回多個值時要用括號包起來
~~~
package main
import "fmt"
func test(num1,num2 int) (int,int){
res1 := num1 + num2
res2 := num1 - num2
return res1,res2
}
func main(){
a,b := test(20,10)
fmt.Printf("a = %v\nb = %v",a,b)
}
~~~
**運行結果**
`a = 30`
`b = 10`
<br>
<br>
>[]#### **只接受一個返回值(_)來忽略**
~~~
package main
import "fmt"
func test(num1,num2 int) (int,int){
res1 := num1 + num2
res2 := num1 - num2
return res1,res2
}
func main(){
a,_ := test(20,10)
fmt.Printf("a = %v\nb = %v",a)
}
~~~
**運行結果**
a = 30
b = %!v(MISSING)
<br>
<br>
>[info]## **二、函數遞歸調用**
說名:函數體內又調用了本身,稱之為遞歸調用
~~~
package main
func test(n int){
if n > 2{
n--
test(n)
}
}
func main(){
test(4)
}
~~~
~~~
package main
import "fmt"
func test(n int){
if n > 2{
n--
test(n)
}else{
fmt.Println("n = ",n)
}
}
func main(){
test(4)
}
~~~
<br>
>[success]### **2.1 遞歸調用的原則**
1)執行一個函數時,就創建一個新的受保護的獨立空間(新函數棧)
2)函數的局部變量是獨立的,不會相互影響
3)遞歸必須向退出遞歸的條件逼近,否則就是無限遞歸,死龜了:)
4)當一個函數執行完畢,或者遇到return,就會返回,遵守誰調用,就將結果返回
給誰,同時當函數執行完畢或者返回時,該函數本身也會被系統銷毀
>[success]## **三、init函數**
>[info] ### **3.1 基本介紹**
每一個包都有一個init函數,該函數在main函數之前執行,
>[info] ### **3.2 案例演示**
~~~
package main
import "fmt"
func init(){
fmt.Println("init")
}
func main(){
fmt.Println("main")
}
~~~
**運行結果**
**init**
**main**
>[warning] ### **注意事項**
1)go中在當前文件中的執行順序,全局變量 ?---> init函數----> main函數
代碼如下:
~~~
package main
import "fmt"
var sum = test()
func test() int{
fmt.Println("test()")
return 100
}
func init(){
fmt.Println("init")
}
func main(){
fmt.Println("main")
}
~~~
**運行結果**
**test()**
**init**
**main**
<br>
2)如果引入其他文件的全局變量和init函數,那么執行順序如下:
:-: 
<br>
<br>
>[success]## **四、匿名函數**
>[info]### **4.1 基本介紹**
Go支持匿名函數,匿名函數就是沒有名字的函數,如果我們某個函數只是希望使用一次,可以考慮使用匿名函數,匿名函數也可以實現多次調用。
>[info] ### **4.2 匿名函數使用方式**
1)匿名函數使用方式1
在定義匿名函數時就直接調用,[案例演示]
~~~
package main
import "fmt"
func main(){
res := func(n,m int) int{
return n + m
}(10,20) // 這里是直接調用
fmt.Println(res)
}
~~~
2)匿名函數使用方式2
將匿名函數賦給一一個變量(函數變量),再通過該變量來調用匿名函數
此時匿名函數`func(n3 int , n4 int) int 賦值給了res`
res的數據類型是一個函數類型,通過調用ret來完成調用
【案例演示】
~~~
package main
import "fmt"
func main(){
res := func(n,m int) int{
return n + m
}
sum := res(100,200)
fmt.Println("sum = ",sum)
sum1 := res(30,20)
fmt.Println("sum1 = ",sum1)
}
~~~
**運行結果**
`sum = 300`
`sum1 = 50`
<br>
<br>
全局匿名函數
如果將匿名函數賦給一個全局變量,那么這個匿名函數,就成為一個全局匿名函數,可以在程序有效。[案例演示]
~~~
package main
import "fmt"
var (
res = func(n,m int) int{
return n + m
}
)
func main(){
ret := res(200,200)
fmt.Println(ret)
}
~~~
**運行結果**
400
<br>
<br>
>[success]## **五、閉包**
>[info]### **5.1閉包介紹:**
閉包就是一個函數與其相關的引用環境,組合成一個整體
>[info]### **5.2** **strings 和 strconv 包**
>[info]### **5.3案例演練:**
~~~
package main
import "fmt"
func Addper()func(int)int{
var sum = 10
return func(i int) int {
fmt.Printf("匿名函數接收到了%v\n",i)
sum = sum + i
return sum
}
}
func main(){
f := Addper()
fmt.Println(f(1))
fmt.Println(f(10))
fmt.Println(f(100))
}
~~~
**運行結果**
匿名函數接收到了1
11
匿名函數接收到了10
21
匿名函數接收到了100
121
<br>
返回的是一個匿名函數。但是這個匿名函數引用到函數外的i .因此這個匿名函數就和i形成一個整體,構成閉包。(第6行到10行就是閉包)
1)大家可以這樣理解:閉包是類.函數是操作,i是字段。函數和它使用到n構成閉包。
2)當我們反復的調用f函數時,因為i是初始化一次,因此每調用一次就進行累計。
3)我們要搞清楚閉包的關鍵,就是要分析出返回的函數它使用(引用)到哪些變量,因為函數和它引用到的變量共同構成閉包。
<br>
<br>
>[info]### **5.4閉包的最佳實踐**
請編寫一個程序,具體要求如下
1)編寫一個函數makefuffix(suffix string)可以接收一個文件后綴名(比如.jpg),并回一個閉包
2)調用閉包,可以傳入一個文件名,如果該文件名沒有指定的后綴(比如.jpg) ,則返
3)回文件名.jpg,如果已經有.jpg后綴,則返回原文件名。要求使用閉包的方式完成
4)strings.HasSuffix(判斷后綴名)
func [HasSuffix](https://github.com/golang/go/blob/master/src/strings/strings.go?name=release#376 "View Source")[](https://studygolang.com/static/pkgdoc/pkg/strings.htm#pkg-index)
~~~
package main
import (
"fmt"
"strings"
)
func MaKeSuffix(suffix string)func(string)string{
return func(name string) string {
if !strings.HasSuffix(name,suffix){
return name + suffix
}
return name
}
}
func main(){
res := MaKeSuffix(".jpg")
fmt.Println(res("witer"))
fmt.Println(res("f"))
fmt.Println(res("image"))
}
~~~
**運行結果**
witer.jpg
f.jpg
image.jpg
<br>
<br>
>[success]## **六、函數 defer**
>[info]### **6.1 基本介紹**
在函數中,程序員經常需要創建資源(比如:數據庫連接、文件句柄、鎖等),為了在函數執行完畢后,及時的釋放資源,Go的設計者提供defer (延時機制)
>[info]### **6.2 案例演示**
~~~
package main
import "fmt"
func test(n,m int)int{
defer fmt.Println("n",n)
defer fmt.Println("m",m)
res := n + m
fmt.Println("res = ",res)
return res
}
func main(){
f := test(10,20)
fmt.Println(f)
}
~~~
**運行結果**
res = 30
m 20
n 10
30
<br>
<br>
>[info]### **defer細節**
1)當go執行到一個defer時, 不會立即執行defer后的語句,而是將defer后的語句壓入到一個棧中【我為了方便,暫時稱該棧為defer棧】然后繼續執行函數下一個語句
2)當函數執行完畢后,在從defer棧中,依次從棧頂取出語句執行(注:遵守棧先入后出的機制),所以同學們看到前面案例輸出的順序。
3)在defer 將語句放入到棧時,也會將相關的值拷貝同時入棧。請看一段代碼:
4)在golang編程中的通常做法是,創建資源后,比如(打開了文件,獲取了數據庫的鏈接,或者是鎖資源),可以執行 `defer file.Close() defer connect .CloseO
`
5)在defer后,可以繼續使用創建資源
6)當函數完畢后,系統會依次從defer棧中,取出語句,關閉資源三造,
7)這種機制,非常簡潔,程序員不用再為在什么時機關閉資源而煩心。
<br>
<br>
>[success]## **七、函數參數的傳遞方式**
:-: 
>[info]### **7.1 基本介紹**
我們在講解函數注意事項和使用細節時,已經講過值類型和引用類型了,這里
我們再系統總結一下,因為這是重難點,**值類型參數默認就是值傳遞**,而引用類型
**參數默認就是引用傳遞。**
兩種傳遞方式
1)值傳遞
2)引用傳遞
其實,不管是值傳遞還是引用傳遞,傳遞給函數的都是變量的副本,不同的是,值傳遞的是值的拷貝,引用傳遞的是地址的拷貝,一般來說,**地址拷貝效率高**,因為數據量小,而值拷貝訣定拷貝的數據大小,數據越大,效率越低。
>[info]### **7.2 值類型和引用類型**
1)值類型: 基本數據類型int系列, float系列, bool, string、數組和結構體struct
2)引用類型: 指針、slice切片、 map、管道chan、interface 等都是引用類型
    ●函數參數的傳遞方式
        1)值類型默認是值傳遞: 變量直接存儲值,內存通常在棧中分配[案例:畫出示意圖]
:-: 
        2)引用類型默認是引用傳遞:變量存儲的是一個地址,這個地址對應的空間才真正存儲數據(值),內存通常在堆上分配,當沒有任何變量引用這個地址時,該地址對應的數據空間就成為一個垃圾,由GC來回收。[案例, 并畫出示意圖]
:-: 
>[success]## **八、變量的作用域**
>[info] ### 基本介紹
1)函數內部聲明/定義的變量叫局部變量,作用域僅限于函數內部。
2)函數外部聲明/定義的變量叫全局變量,作用域在整個包都有效,如果其首字母為大寫,則作用域在整個程序有效。
~~~
package main
import "fmt"
var(
age = 23
Name = "Boy"
)
func test(){
age := 18
Name := "jack"
fmt.Printf("age = %v\nName = %v",age,Name)
}
func main(){
fmt.Println(age)
fmt.Println(Name)
test()
}
~~~
**運行結果**
23
Boy
age = 18
Name = jack
1)如果變量是在一個代碼塊,比如for/if中, 那么這個變量的的作用域就在該代碼塊
**練習題**
如下代碼,會報錯
:-: 
**函數的練習**
1)在終端輸入一個整數,打印出相應的金字塔
:-: 
>[success]## **九、string常用的函數**
說明:字符串在我們程序開發中,使用的是非常多的,常用的函數需要掌握[帶看手冊或者官方編程指南]:
>[info]**9,1 統計字符串的長度**
按字節len(str)-----(len是按照字節遍歷,并非按照字符)
~~~
func main(){
c := "hello"
fmt.Println(len(c))
}
~~~
>[info]**9.2 字符串遍歷**
同時處理有中文的問題r:= **[ ]rune(str)**,;例如遍歷 a:=“hallo北京”
~~~
package main
import "fmt"
func main(){
c := "hello北京"
res := []rune(c)
for i := 1;i < len(res);i++{
fmt.Printf("hello北京的長度是:%c\n",res[i])
}
}
~~~
>[info]**9.3 字符串轉整數**:
n, err := strconv.Atoi("12")
~~~
package main
import (
"fmt"
"strconv"
)
func main(){
sum := "12345"
re,err := strconv.Atoi(sum)
if err != nil{
fmt.Println("轉換錯誤",err)
}else{
fmt.Println("轉換成功",re)
}
}
~~~
>[info]**9.4 整數轉字符串**?
str = strconv.Itoa(12345)
~~~
package main
import (
"fmt"
"strconv"
)
func main(){
sum := 123456
str := strconv.Itoa(sum)
fmt.Printf("str value is【%v】\nstr type is【%T】",str,str)
}
~~~
**運行結果**
str value is【123456】
str type is【string】
>[info] **9.5 字符串轉[]byte**
var bytes = [ ]byte("hello go")
~~~
package main
import (
"fmt"
)
func main(){
sum := []byte("hello")
fmt.Println(sum)
}
~~~
**運行結果**
`[104 101 108 108 111]`
>[info] **9.6 []byte 轉字符串:**
**`str = string( [ ]byte{97, 98, 99})`**
~~~
package main
import (
"fmt"
)
func main(){
sum := string([]byte{97, 98, 99})
fmt.Printf("sum value %v\nsum type is %T",sum,sum)
}
~~~
**運行結果**
**sum value abc**
**sum type is string**
- Golang語言之旅
- 第一章:初始小節以及安裝
- 一:Golang語言特性
- 二:Windows上安裝Go語言開發包
- 三:在Mac OS上安裝Go語言開發包
- 第二章:GO語言注意事項
- 一:Dos的常用指令
- 第三章:Go初識小菜
- 一:Go語言之變量與常量
- 二:Go內置值-引用類型
- 三:基本的數據類型
- 四:字符串(char)
- 五:布爾類型(bool)
- 六:字符串類型(string)
- 七:基本數據類型的默認值
- 八:基本數據類型的互相轉換
- 九:基本數據類型和string類型的相互轉換
- 十:Golang指針
- 十一:值類型和引用類型
- 十二:標識符和命名規范
- 十三:系統保留關鍵字and預定義標識符
- 十四:fmt常用方法解析
- 第四章:Go運算符
- 一:運算符的基本介紹
- 二:算術運算符
- 2.1:算數運算符細節
- 三:關系運算符
- 3.1:關系運算符細節
- 四:邏輯運算符
- 4.1:邏輯運算符細節及案例
- 五:Go賦值運算符
- 5.1:案例演示賦值運算符的基本使用
- 5.2:賦值運算符的特點
- 六:Go位運算符
- 七:其他運算符
- 八:運算符的優先級
- 九:控制臺輸入語句
- 十:進制
- 十一:位運算
- 第五章:流程控制大綱
- 一:if語句
- 二:switch語句
- 三:for循環
- 第六章:函數-包-錯誤處理
- 一:Go函數
- 二:Go包
- 三:匿名函數
- 四:閉包
- 五:函數defer
- 六:函數參數的傳遞方式
- 七:變量的作用域
- 八:時間和日期相關函數
- 九:new和recover異常
- 十:數組(Array)切片(Section)
- 十一:切片(slice)
- 十二:3 數組的排序和查找
- 第七章:Map
- 第一節:Map基礎認識
- 第二節:Map初始化和make
- 第三節:Map增刪改查
- 第四節:Map的切片
- 第五節:Map的注意事項
- 第八章:面向對象(上)
- 第一節:結構體(值類型)
- 第二節:方法
- 第三節:面向對象編程應用實例
- 第九章:面向對象(下)
- 第一節:面向對象之抽象
- 第二節:面向對象之繼承
- 第三節:面向對象之多態
- 第四節:接口
- 第十章:文件操作
- 第一節:文件基本介紹
- 第二季:寫文件實例操作
- 第三節:JSON
- 第十一章:單元測試
- 第一節:單元測試介紹
- 第二節:單元測試案例
- 第三節:單元測試總結
- 第四節:單元測試案例
- 第十二章:goroutine和channel
- 第一節:goroutine基本介紹
- 第二節:goroutine入門案例
- 第三節:goroutione調度模型
- 第四節:Golang設置運行的CPU數量
- 第十二章:channel
- 第一節:channel基本介紹
- 第二節:channel基本使用
- 第三節:channel案例演示
- 第四節:channel 使用的注意事項
- 第五節:channel練習題
- 第六節:channel的遍歷和關閉
- 第七節:goroutione和channel結合
- 第八節:channel細節處理
- 第十二章:并發模式
- 第十三章:反射reflect
- 第一節:反射基本介紹
- 第二節:反射重要的函數和概念
- 第三節:反射快速入門案例
- 第四節:反射注意事項
- 第五節:反射練習題
- 第六節:反射最佳實踐