? ? ? ? 大家好,這是我給大家準備的新的一期專欄,專門講golang,從入門到精通各種框架和中間件,工具類庫,希望對go有興趣的同學可以訂閱此專欄。
go基礎 。
Go文件名:
? ? ? 所有的go源碼都是以 ".go" 結尾,不過我們有時候為了支持多平臺,多版本,或者在不同的平臺,CPU下做不同的邏輯處理,我們對go的文件名的命名方式又有一些要求:
1、平臺區分
文件名_平臺。
例: file_windows.go, file_unix.go
可選為:windows, unix, posix, android, bsd, solaris, linux等2、測試單元
文件名test.go或者 文件名平臺_test.go。
例: path_test.go, path_windows_test.go3、版本區分(猜測)
文件名_版本號等。
例:point_windows_1.4.go4、CPU類型區分, 匯編用的多
文件名_(平臺:可選)_CPU類型.
例:point_linux_amd64.go
可選:amd64, none, 386, arm, arm64, x86等。
Go語言命名和關鍵字?
1.Go的函數、變量、常量、自定義類型、包(package)
的命名方式遵循以下規則:
1)首字符可以是任意的Unicode字符或者下劃線2)剩余字符可以是Unicode字符、下劃線、數字3)字符長度不限
2.Go只有25個關鍵字
break default func interface selectcase defer go map structchan else goto package switchconst fallthrough if range typecontinue for import return var
3.Go還有37個保留字
Constants: true false iota nilTypes: int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptrfloat32 float64 complex128 complex64bool byte rune string errorFunctions: make len cap new append copy close deletecomplex real imagpanic recover
Go語言聲明:
go有四種主要聲明方式:
var(聲明變量), const(聲明常量), type(聲明類型) ,func(聲明函數)。
Go的程序是保存在多個.go文件中,文件的第一行就是 package(包名) 聲明,用來說明該文件屬于哪個包(package),package聲明下來就是import聲明,再下來是類型,變量,常量,函數的聲明。
????????go是使用包來組織源代碼的,包是多個go源碼的集合,是一種高級的代碼復用方案。golang為我們提供了很多內置的包,如 fmt、os、io 等。
????????任何源代碼文件必須屬于某個包,同時源碼文件的第一行有效代碼必須是package pacakgeName 語句,通過該語句聲明自己所在的包。
? ? ? ? ?正如我們上一篇文章寫的最簡單的hello world程序輸出一樣:
package mainimport "fmt"
func main(){fmt.Println("hello world")
}
? ? ? ? ?我們在文件的最開始是通過package? main的方式來說明該文件屬于main包,接下來我們需要知道包是什么,怎么使用它(其實學過java,或者python等語言的對包的概念和使用并不陌生)
包的基本概念
? ? ? ? Go 的包借助了目錄樹組織形式,一般包的名稱就是其源文件所在的目錄,雖然Go語言沒有強制要求包名必須和其所在的目錄名同名,但還是建議包名和所在目錄同名,這樣結構更清晰。
包可以定義在很深的目錄中,包名的定義是不包括目錄路徑的,但是包在引用時一般使用全路徑引用。比如在GOPATH/src/a/b/ 下定義一個包 c。在包 c 的源碼中只需聲明為package c,而不是聲明為package a/b/c,但是在導入 c 包時,需要帶上路徑,例如import “a/b/c”。
包的習慣用法:
- 包名一般是小寫的,使用一個簡短且有意義的名稱。
- 包名一般要和所在的目錄同名,也可以不同,包名中不能包含- 等特殊符號。
- 包一般使用域名作為目錄名稱,這樣能保證包名的唯一性,比如 GitHub 項目的包一般會放到GOPATH/src/github.com/userName/projectName 目錄下。
- 包名為 main 的包為應用程序的入口包,編譯不包含 main 包的源碼文件時不會得到可執行文件。
- 一個文件夾下的所有源碼文件只能屬于同一個包,同樣屬于同一個包的源碼文件不能放在多個文件夾下。
包的導入
要在代碼中引用其他包的內容,需要使用 import?導入使用的包。具體語法如下:
import "包的路徑"
注意事項:
- import 導入語句通常放在源碼文件開頭包聲明語句的下面;
- 導入的包名需要使用雙引號包裹起來;
- 包名是從GOPATH/src/ 后開始計算的,使用/ 進行路徑分隔。
包的導入有兩種寫法,分別是單行導入和多行導入。
單行導入
import "package1"
import "package2"
多行導入
import ("package1""package2"
)
在使用習慣和代碼的可讀性上,我們都習慣于使用多行導入更方便于我們管理使用。?
包的導入路徑
包的引用路徑有兩種寫法,分別是全路徑導入和相對路徑導入
包的引用格式
包的引用有四種格式,下面以 fmt 包為例來分別演示一下這四種格式。
1) 標準引用格式
import "fmt"
此時可以用fmt.作為前綴來使用 fmt 包中的方法,這是常用的一種方式。
示例代碼如下:
package main
import "fmt"
func main() {fmt.Println("Hello world")
}
2) 自定義別名引用格式
在導入包的時候,我們還可以為導入的包設置別名,如下所示:
import F "fmt"
?其中 F 就是 fmt 包的別名,使用時我們可以使用F.來代替標準引用格式的fmt.來作為前綴使用 fmt 包中的方法。
示例代碼如下:
package main
import F "fmt"
func main() {F.Println("Hello world")
}
3) 省略引用格式
import . "fmt"
這種格式相當于把 fmt 包直接合并到當前程序中,在使用 fmt 包內的方法是可以不用加前綴fmt.,直接引用。
示例代碼如下:
package main
import . "fmt"
func main() { Println("Hello world")
}
4) 匿名引用格式
在引用某個包時,如果只是希望執行包初始化的 init 函數,而不使用包內部的數據時,可以使用匿名引用格式,如下所示:
import _ "fmt"
匿名導入的包與其他方式導入的包一樣都會被編譯到可執行文件中。
使用標準格式引用包,但是代碼中卻沒有使用包,編譯器會報錯。如果包中有 init 初始化函數,則通過import _ “包的路徑” 這種方式引用包,僅執行包的初始化函數,即使包沒有 init 初始化函數,也不會引發編譯器報錯。
示例代碼如下:
package main
import (_ "github.com/astaxie/beego""fmt"
)
func main() {fmt.Println("Hello world")
}
注意:
- 一個包可以有多個 init 函數,包加載時會執行全部的 init 函數,但并不能保證執行順序,所以不建議在一個包中放入多個 init 函數,將需要初始化的邏輯放到一個 init 函數里面。
- 包不能出現環形引用的情況,比如包 a 引用了包 b,包 b 引用了包 c,如果包 c 又引用了包 a,則編譯不能通過。
- 包的重復引用是允許的,比如包 a 引用了包 b 和包 c,包 b 和包 c 都引用了包 d。這種場景相當于重復引用了 d,這種情況是允許的,并且 Go 編譯器保證包 d 的 init 函數只會執行一次。
?這塊大家可以多練習練習嘗試下,凡是和語言相關的知識在初步學習過程中要不斷地練習和嘗試,就會有不一樣的角度的理解。
包的初始化順序
在分享包的初始化順序之前,有必要了解下init函數和main函數,?
init
函數
go語言中init
函數用于包(package)
的初始化,該函數是go語言的一個重要特性。
有下面的特征:
1 init函數是用于程序執行前做包的初始化的函數,比如初始化包里的變量等2 每個包可以擁有多個init函數3 包的每個源文件也可以擁有多個init函數4 同一個包中多個init函數的執行順序go語言沒有明確的定義(說明)5 不同包的init函數按照包導入的依賴關系決定該初始化函數的執行順序6 init函數不能被其他函數調用,而是在main函數執行之前,自動被調用
main
函數
? ?Go語言程序的默認入口函數(主函數):func main()
? ? 函數體用{}一對括號包裹。
func main(){//函數體}
init
函數和main
函數的異同
相同點:兩個函數在定義時不能有任何的參數和返回值,且Go程序自動調用。不同點:init可以應用于任意包中,且可以重復定義多個。main函數只能用于main包中,且只能定義一個。
兩個函數的執行順序:
對同一個go文件的init()
調用順序是從上到下的。
對同一個package中不同文件是按文件名字符串比較“從小到大”順序調用各文件中的init()
函數。
對于不同的package
,如果不相互依賴的話,按照main包中”先import
的后調用”的順序調用其包中的init()
,如果package
存在依賴,則先調用最早被依賴的package
中的init()
,最后調用main
函數。
為了進一步看下init函數和main函數的初始化順序,我們可以看下這段代碼:
package mainimport ("fmt"
)
const (CFG_VISIT = "visit.json"
)var (gameStatus = 0
)func init(){fmt.Println("go init")
}func main(){fmt.Println("hello world")
}
對于import 包,const 初始化, var初始化,init函數,main函數的調用順序是什么樣的?
實際上,整個程序的執行流程可以用下面的圖來表示:
?
總結下來,go語言包的初始化有如下特點:
- 包初始化程序從 main 函數引用的包開始,逐級查找包的引用,直到找到沒有引用其他包的包,最終生成一個包引用的有向無環圖。
- golang編譯器會將有向無環圖轉換為一棵樹,然后從樹的葉子節點開始逐層向上對包進行初始化。
- 單個包的初始化過程如上圖所示,先初始化常量,然后是全局變量,最后執行包的 init 函數。
內置類型
值類型:
boolint(32 or 64), int8, int16, int32, int64uint(32 or 64), uint8(byte), uint16, uint32, uint64float32, float64stringcomplex64, complex128array -- 固定長度的數組
引用類型:(指針類型)
slice -- 序列數組(最常用)map -- 映射chan -- 管道
內置函數
Go 語言擁有一些不需要進行導入操作就可以使用的內置函數。它們有時可以針對不同的類型進行操作,例如:len、cap 和 append,或必須用于系統級的操作,例如:panic。因此,它們需要直接獲得編譯器的支持。