[TOC]
# 導包
1. 點操作? ?有時候會看到如下的方式導入包? ? ?import( . “fmt” )?
這個點操作的含義就是這個包導入之后在你調用這個包的函數時,你可以省略前綴的包名,也就是前面你調用的fmt.Println(“hello world”)? 可以省略的寫成Println(“hello world”)
? ? ? 2. 別名操作? ?別名操作顧名思義可以把包命名成另一個用起來容易記憶的名字
? ? ? ? ? ?import( f “fmt” )? ?別名操作調用包函數時前綴變成了重命名的前綴,即f.Println(“hello world”)
? ? ? 3.? _操作? ?這個操作經常是讓很多人費解的一個操作符,請看下面這個import
? ? ? ? ? import ( “database/sql” _ “github.com/ziutek/mymysql/godrv” )?
?
? ? ? ? _操作其實只是引入該包。當導入一個包時,它所有的init()函數就會被執行,但有些時候并非真的需要使用這些包,僅僅是希望它的init()函數被執 行而已。這個時候就可以使用_操作引用該包了。即使用_操作引用包是無法通過包名來調用包中的導出函數,而是只是為了簡單的調用其init函數()
# 函數定義
~~~
func 函數名(/*參數列表*/) (ol type1, ol2 type2 /*返回類型*/) {
//函數體
return v1, v2 //返回多個值
}
~~~
函數名小寫為private,大寫為public
參數列表不支持默認參數
返回類型,可以只有類型沒有變量名
# 不定參數類型
參數個數為不定數量
~~~
...type只能作為函數的參數類型存在,并且是最后一個參數
~~~
~~~
func 函數名(args ...int) {
}
~~~
~~~
func MyFunc(args ...int) {
//獲取用戶傳遞參數的個數
fmt.Println("len(args) = ", len(args))
for i := 0; i<len(args); i++ {
fmt.Printf("args[%d] = %d\n", i, args[i])
}
fmt.Println("==========================")
//返回2個值一個是下標,一個是下標對應的數
for i, data := range args {
fmt.Printf("args[%d] = %d\n", i, data)
}
}
func main() {
MyFunc(1, 2, 3)
}
~~~
參數間傳遞
~~~
func MyFunc(args ...int) {
//返回2個值一個是下標,一個是下標對應的數
for i, data := range args {
fmt.Printf("args[%d] = %d\n", i, data)
}
fmt.Println("=============================")
}
func test(args ...int) {
MyFunc(args...)
//args[0]~args[2](不包括數字args[2])
MyFunc(args[:2]...)
//從args[2]開始(包括本身),把后面所有元素傳遞過去
MyFunc(args[2:]...)
}
func main() {
test(1, 2, 3)
}
~~~
# 多個返回值
~~~
func test() (int, int, int) {
return 1, 2, 3
}
func main() {
a, b, c := test()
fmt.Println(a, b, c)
}
~~~
~~~
func MaxAndMin(a, b int) (max, min int) {
if a > b{
max = a
min = b
} else {
max = b
min = a
}
return
}
func main() {
max, min := MaxAndMin(1, 2)
//通過匿名變量丟棄某個返回值
//max, _ := MaxAndMin(1, 2)
fmt.Print(max, min)
}
~~~
# 函數類型
函數也是一種類型,我們可以用type來定義他,他的類型就是所有擁有相同的參數,相同的返回值的一種類型
~~~
type FuncType func(int, int) int //聲明一個函數類型, func后面沒有函數名
func Add(a, b int) int {
return a + b
}
func main() {
var fTest FuncType
fTest = Add
result := fTest(10, 20)
fmt.Print(result)
}
~~~
# 函數作為參數
傳遞的函數要形參和返回值類型個數一樣就可以
~~~
func sub(a, b int) int {
return a-b
}
func calc(a, b int, op func(int, int)int) int {
return op(a, b)
}
func main() {
sum := calc(300, 200, sub)
fmt.Println(sum)
}
~~~
# 回調函數
回調函數,函數里面有一個參數是函數類型
~~~
type FuncType func(int, int) int //聲明一個函數類型, func后面沒有函數名
func Add(a, b int) int {
return a + b
}
func Calc(a, b int, fTest FuncType) (result int) {
fmt.Println("calc")
result = fTest(a, b)
return
}
func main() {
a := Calc(1, 2, Add)
fmt.Println("a = ", a)
}
~~~
# defer
**如果有return,就是在reutrn之前執行**
延遲調用,main函數結束前
~~~
func main() {
defer fmt.Println("bbbbbb")
fmt.Println("aaaaaaaa")
}
~~~
輸出
~~~
aaaaaaaa
bbbbbb
~~~
如果一個函數有多個defer,他們會以**后進先出**順序執行,哪怕某個函數或某個延遲調用發生錯誤,這些調用依舊會被執行
~~~
func test(x int) int {
a := 100 / x
return a
}
func main() {
fmt.Println("aaaaaaaa")
defer fmt.Println("bbbbbb")
defer test(0)
defer fmt.Println("CCCCC")
defer fmt.Println("DDDDDD")
}
~~~
輸出
~~~
aaaaaaaa
DDDDDD
CCCCC
bbbbbb
panic: runtime error: integer divide by zero
~~~
**defer和匿名函數結合使用**
~~~
func main() {
a := 10
b := 20
defer func() {
a = 12
b = 12
fmt.Printf("內部a = %d, b = %d\n", a, b)
}()
fmt.Printf("外部部a = %d, b = %d\n", a, b)
}
~~~
輸出
~~~
外部部a = 10, b = 20
內部a = 12, b = 12
~~~
~~~
func main() {
a := 10
b := 20
defer func(a, b int) {
fmt.Printf("內部a = %d, b = %d\n", a, b)
}(a, b) //相當于提前放10和20
a = 11
b = 21
fmt.Printf("外部部a = %d, b = %d\n", a, b)
}
~~~
輸出
~~~
外部部a = 11, b = 21
內部a = 10, b = 20
~~~
# 獲取命令行參數
~~~
func main() {
//接收用戶傳遞的參數,都是以字符串方式傳遞
list := os.Args
//長度
n := len(list)
fmt.Println(n)
for i, data := range list {
fmt.Printf("args[%d] = %s\n", i, data)
}
}
~~~
# 函數簽名
在 Go 語言中,函數可是一等的(first-class)公民,函數類型也是一等的數據類型。這是什么意思呢?
簡單來說,這意味著函數不但可以用于封裝代碼、分割功能、解耦邏輯,還可以化身為普通的值,在其他函數間傳遞、賦予變量、做類型判斷和轉換等等,就像切片和字典的值那樣。
而更深層次的含義就是:函數值可以由此成為能夠被隨意傳播的獨立邏輯組件(或者說功能模塊)。

這里,我先聲明了一個函數類型,名叫Printer。
注意這里的寫法,在類型聲明的名稱右邊的是func關鍵字,我們由此就可知道這是一個函數類型的聲明。
在func右邊的就是這個函數類型的參數列表和結果列表。其中,參數列表必須由圓括號包裹,而只要結果列表中只有一個結果聲明,并且沒有為它命名,我們就可以省略掉外圍的圓括號。
書寫函數簽名的方式與函數聲明的是一致的。只是緊挨在參數列表左邊的不是函數名稱,而是關鍵字func。這里函數名稱和func互換了一下位置而已。
> 函數的簽名其實就是函數的參數列表和結果列表的統稱,它定義了可用來鑒別不同函數的那些特征,同時也定義了我們與函數交互的方式。
注意,各個參數和結果的名稱不能算作函數簽名的一部分,甚至對于結果聲明來說,沒有名稱都可以。
只要兩個函數的參數列表和結果列表中的元素順序及其類型是一致的,我們就可以說它們是一樣的函數,或者說是實現了同一個函數類型的函數。
嚴格來說,函數的名稱也不能算作函數簽名的一部分,它只是我們在調用函數時,需要給定的標識符而已。
我在下面聲明的函數printToStd的簽名與Printer的是一致的,因此前者是后者的一個實現,即使它們的名稱以及有的結果名稱是不同的。
- 基礎
- 簡介
- 主要特征
- 變量和常量
- 編碼轉換
- 數組
- byte與rune
- big
- sort接口
- 和mysql類型對應
- 函數
- 閉包
- 工作區
- 復合類型
- 指針
- 切片
- map
- 結構體
- sync.Map
- 隨機數
- 面向對象
- 匿名組合
- 方法
- 接口
- 權限
- 類型查詢
- 異常處理
- error
- panic
- recover
- 自定義錯誤
- 字符串處理
- 正則表達式
- json
- 文件操作
- os
- 文件讀寫
- 目錄
- bufio
- ioutil
- gob
- 棧幀的內存布局
- shell
- 時間處理
- time詳情
- time使用
- new和make的區別
- container
- list
- heap
- ring
- 測試
- 單元測試
- Mock依賴
- delve
- 命令
- TestMain
- path和filepath包
- log日志
- 反射
- 詳解
- plugin包
- 信號
- goto
- 協程
- 簡介
- 創建
- 協程退出
- runtime
- channel
- select
- 死鎖
- 互斥鎖
- 讀寫鎖
- 條件變量
- 嵌套
- 計算單個協程占用內存
- 執行規則
- 原子操作
- WaitGroup
- 定時器
- 對象池
- sync.once
- 網絡編程
- 分層模型
- socket
- tcp
- udp
- 服務端
- 客戶端
- 并發服務器
- Http
- 簡介
- http服務器
- http客戶端
- 爬蟲
- 平滑重啟
- context
- httptest
- 優雅中止
- web服務平滑重啟
- beego
- 安裝
- 路由器
- orm
- 單表增刪改查
- 多級表
- orm使用
- 高級查詢
- 關系查詢
- SQL查詢
- 元數據二次定義
- 控制器
- 參數解析
- 過濾器
- 數據輸出
- 表單數據驗證
- 錯誤處理
- 日志
- 模塊
- cache
- task
- 調試模塊
- config
- 部署
- 一些包
- gjson
- goredis
- collection
- sjson
- redigo
- aliyunoss
- 密碼
- 對稱加密
- 非對稱加密
- 單向散列函數
- 消息認證
- 數字簽名
- mysql優化
- 常見錯誤
- go run的錯誤
- 新手常見錯誤
- 中級錯誤
- 高級錯誤
- 常用工具
- 協程-泄露
- go env
- gometalinter代碼檢查
- go build
- go clean
- go test
- 包管理器
- go mod
- gopm
- go fmt
- pprof
- 提高編譯
- go get
- 代理
- 其他的知識
- go內存對齊
- 細節總結
- nginx路由匹配
- 一些博客
- redis為什么快
- cpu高速緩存
- 常用命令
- Go 永久阻塞的方法
- 常用技巧
- 密碼加密解密
- for 循環迭代變量
- 備注
- 垃圾回收
- 協程和纖程
- tar-gz
- 紅包算法
- 解決golang.org/x 下載失敗
- 逃逸分析
- docker
- 鏡像
- 容器
- 數據卷
- 網絡管理
- 網絡模式
- dockerfile
- docker-composer
- 微服務
- protoBuf
- GRPC
- tls
- consul
- micro
- crontab
- shell調用
- gorhill/cronexpr
- raft
- go操作etcd
- mongodb