寫給大忙人看的Go語言(一)
Tips
寫給大忙人看的Golang教程(一)
閱讀本文之前,我認為你已經掌握其他語言基礎並寫出一個簡單的專案。
(1)Golang程式設計注意事項
.go main()
(2)Golang中的常用轉義字元
\t \n \\ \" \r
(3)註釋方式
// 註釋內容 /* 註釋內容 */
(4)Golang的程式碼規範
- 儘量使用行註釋註釋整個方法或語句
- 使用Tab縮排
- 使用
gofmt -w
格式化程式碼 - 運算子兩側加空格
-
Golang的程式碼風格:
// HelloWorld.go package main import "fmt" func main() { fmt.Println("Hello World") }
- 一行程式碼最好不要超過80個字元
(5)官方程式設計指南
- Go的官方網站 ofollow,noindex" target="_blank">https://golang.org
- Go的標準庫英文官方文件 https://golang.org/pkgdoc
- Go的標準庫中文官方文件 https://studygolang.com/pkgdoc
(6)變數
- 使用
var
關鍵字定義變數:var 變數名稱 資料型別
- 使用型別推導:
var 變數名稱 = 值
- 省略
var
關鍵字:變數名稱 := 值
, 變數名稱不應該是已經定義過的 -
變數名稱 := 值
等同於var 變數名稱 資料型別 = 值
- 多變數宣告:
var 變數名稱, 變數名稱... 資料型別
- 多變數賦值:
var 變數名稱, 變數名稱, ... = 值, 值, ...
- 省略
var
關鍵字多變數宣告和賦值:變數名稱, 變數名稱, ... := 值, 值, ...
- 宣告全域性變數:
var ( 變數名稱 = 值 ... )
(7)Golang支援的資料型別
-
使用
unsafe.Sizeof()
檢視變數佔用的空間package main import ( "fmt" "unsafe" ) func main() { var x int = 10 fmt.Println("The x size: ", unsafe.Sizeof(x)) // The x size:8 }
-
float32
表示單精度,float64
表示雙精度 -
字元常量使用單引號
-
Go中的字元編碼使用
UTF-8
型別 -
bool
型別只能取true
或false
-
在Go中字串是不可變的
-
Go支援使用反引號輸出原生字串
- 字串拼接使用加號時最後一個加號需要保留在行尾
(8)基本資料型別轉換
- 數值型別互相轉換:
目標資料型別(變數)
。 - 數值與字串互轉:
- 數字轉字串:使用
fmt.Sprintf()
字串格式化函式。 - 數字轉字串:使用
strconv.FormatBool()
、strconv.FormatInt()
、strconv.FormatUint()
、strconv.FormatFloat()
格式化函式。 - 字串轉數字:使用
strconv.ParseBool()
、strconv.ParseInt()
、strconv.ParseUint()
、strconv.ParseFloat()
格式化函式。
- 數字轉字串:使用
(9)指標資料型別
package main import "fmt" func main() { var i = 10 var ptr *int = &i fmt.Println("變數i的記憶體地址是: ", ptr) // 變數i的記憶體地址是:0xc00004a080 fmt.Println("變數i的儲存內容是: ", *ptr) // 變數i的儲存內容是:10 }
(10) 值型別與引用型別
- 值型別通常存放到棧區。
- 引用型別通常存放在堆區,棧中有堆中的引用。
(11)Golang中的識別符號
_
(12)運算子
- Golang中只有
x++
和x--
,沒有++x
和--x
- 自增自減運算值獨立的語句,不允許類似的:
x := a++
(13)控制檯輸入輸出
fmt.Sacnf() fmt.Sacnln()
(14) 原碼、反碼、補碼
- 計算機中0表示正數,1表示負數
- 計算機中都是用補碼進行運算的,因為CPU只會加法運算
- 正數的原、反、補都是相同的
- 負數的反碼是原碼符號位不變其他位取反
- 負數的補碼是反碼加1
- 0的原、反、補相同
(15)流程控制
(15.1)順序控制
略
(15.2) 分支控制
-
if
語句:if x>12 { } // Golang支援直接在condition中定義變數 if x:=12; x>12 { }
-
if-else
語句:if x:=12; x>20 { }else { }
-
if-else if-else
語句:if x:=12; x>100 { }else if x>50 { }else if x>10 { }else { }
-
switch-case-default
語句:// 每個分支不需要break語句 // switch也支援在condition中直接定義變數 // case支援多個表示式 // 取消break使用fallthrough語句————switch穿透 switch y:=10;y { case 5: // something case 10: // something fallthrough case 20, 25, 30: // something default: // something }
(15.3)迴圈控制
-
for
迴圈for i:=1;i<10;i++ { } // Golang也提供了for-each或for-range類似的迴圈 str := "Hello Golang." for index, value:=range str { // index表示索引 // value表示值 }
-
while
迴圈for { if condition { break } // something }
-
do-while
迴圈for { // something if condition { break } }
(16) 隨機數
// 設定隨機數的種子為當前的系統時間 rand.Seed(time.Now().Unix()) // 生成0-99範圍的隨機數 randomNumber := rand.Intn(100)
(17)break、continue、goto、return語句
-
break
語句在多層巢狀中可以通過標籤指明要終止到哪一層語句塊:
label: for { break label }
-
continue
語句在多層巢狀中可以通過標籤指明要跳出到到哪一層語句塊:
label: for { continue label }
-
goto
語句可以無條件跳轉,容易造成邏輯混亂,一般不主張使用goto
語句:label: for { goto label }
-
return
語句使用者退出函式return // 或 return some
(18)函式
-
函式基本語法:
func functionName (paramsList) (returnList) {}
-
Golang不支援函式過載
-
Golang函式本身也是一種資料型別,可以賦值給變數,那麼該變數也是函式型別
-
Golang函式可以作為實參傳入另一個函式
-
Golang支援自定義資料型別,使用:
type 自定義資料型別名 資料型別
type myfunc func(int)(int, int)
-
支援使用
_
忽略返回值 -
支援可變引數
package main import "fmt" func main() { ret := sum(1, 2, 3) fmt.Println(ret) //6 } // 可變引數 func sum(args...int) int { sum := 0 for i:=0; i<len(args); i++ { sum += args[i] } return sum }
(19)包
包的本質就是一個目錄,Go的每一個檔案都必須屬於一個包。
-
打包:
package packageName
-
匯入包:
import "packageName" // 匯入多個包 import ( "packageName" ... ) // 匯入包時自動從GOPATH下面的src下面引入
-
包支援別名
package main import f "fmt" func main() { f.Println() }
(20) init
函式
- 在Go中每一個原始檔都可以有一個
init
函式,它優先於main
函式執行,被Go框架呼叫。
func init() {}
- 先執行引入的包中的
init
函式再執行main
包中的init
函式
// util.HelloWorld.go package utils import "fmt" func init() { fmt.Println("Util.HelloWorld() init") } func HelloWorld()(){ fmt.Println("Hello World") } // main.test.go package main import ( "StudyGo/utils" "fmt" ) func init() { fmt.Println("Main.main() init") } func main() { utils.HelloWorld() } // Util.HelloWorld() init // Main.main() init // Hello World
(21)匿名函式
-
直接呼叫
func (paramsList)(returnList){ // something }()
-
賦值給一個變數
x := func (paramsList)(returnList){ // something } y := x(paramsList)
(22)閉包
-
閉包就是函式與其相關的引用環境構成的實體
package main import ( "fmt" "strings" ) func main() { fileName := "file" fileSuffix := ".mp3" ms := makeSuffix(fileSuffix) ret := ms(fileName) fmt.Println(ret) } func makeSuffix(suffix string) func(string) string { return func (s string) string { if strings.HasSuffix(s, suffix) { return s }else { return s + suffix } } }
(23)defer 關鍵字
-
defer是Go語言中的延時機制,用於處理關閉檔案控制代碼等資源釋放操作
package main import "fmt" func main() { SayHello() } func SayHello() { defer fmt.Println("Bye.") fmt.Println("Hi.") } // Hi. // Bye.
- 使用defer修飾的語句會壓入棧中,其相關的值也會被壓入棧中
(24) 字串函式
len() strconv.Atoi(s string) (i int, err error) strconv.Itoa(i int) string strconv.FormatInt(i int64, base int) string strings.Contains(s string, sub string) bool strings.Count(s string, sub string) int strings.EqualFold(s_0 string, s_1 string) bool strings.Index(s string, sub string) int strings.LastIndex(s string, sub string) string.Replace(s string, oldSub string, newSub string, n int) string string.ToLower(s string) string.ToUpper(s string) string.Split(s string, sep string) array string.TrimSpace(s string) string string.Trim(s string, sub string) string string.TrimLeft(s string, sub string) string string.TrimRight(s string, sub string) string string.HasPrefix(s string, sub string) bool string.HasSuffix(s string, sub string) bool
(25)時間日期函式
-
time.Time
:表示時間型別 -
time.Now() struct
:獲取當前本地時間 -
time.Now().Year()
:返回年 -
time.Now().Month()
:返回月,使用int(time.Now().Month())
取得數字 -
time.Now().Day()
:返回日 -
time.Now().Hour()
:返回時 -
time.Now().Minute()
:返回分 -
time.Now().Second()
:返回秒 -
time.Now().Format(s string)
:格式化時間資料,2006-01-02 15:04:05
表示格式化的格式字串其中的值不能改變 -
time.Sleep(d Duration)
:休眠函式time.Hour time.Minute time.Second time.Millisecond time.Microsecond time.Nanosecon
-
time.Now().Unix() int64
:返回Unix秒時間戳 -
time.Now().UnixNano() int64
:返回Unix納秒時間戳
(26)內建函式
len() new() make()
(27)錯誤處理
-
在Go中捕獲異常的機制是使用
defer
關鍵字修飾匿名函式,導致匿名函式最後執行,在匿名函式中呼叫recover()
函式,通過返回值是否為nill
來判斷是否發生異常資訊。package main import "fmt" func main() { ret := ComputeNumber(1, 0) fmt.Println(ret) } func ComputeNumber(n_0 int, n_1 int) int { defer func() { if e := recover(); e != nil{ fmt.Println(e) } }() result := n_0 / n_1 return result }
-
自定義錯誤
使用
errors.New(Type) *Type
建立一個error
型別,panic()
接收一個空介面型別,輸出錯誤資訊並結束執行。package main import "errors" func main() { err := readConfigureFile("config.json") if err !=nil { panic(err) // panic: Read config.ini error. } } func readConfigureFile(path string)(err error) { if path != "config.ini" { return errors.New("Read config.ini error.") } else { return nil } }
(28)陣列
在Go中資料是值型別,使用以下方式建立陣列。
var 陣列名稱 [元素個數]資料型別 = [元素個數]資料型別{元素} var 陣列名稱 = [元素個數]資料型別{元素} var 陣列名稱 = [...]資料型別{元素個數} var 陣列名稱 = [...]資料型別{索引:值} for-each/for-range
(29)slice 切片
陣列的長度是固定的,切片 的長度不是固定的。
-
var 切片名稱 []資料型別
-
切片名稱[索引:索引]
-
切片的結構:
[起始資料元素指標, 長度, 容量]
-
通過make建立切片:
var 切片名稱 []資料型別 = make([]資料型別, 長度, 容量)
-
切片支援普通遍歷和
for-range
方式遍歷 -
使用
append()
函式追加元素到切片末尾,容量不夠時自動擴容 -
使用
copy()
函式拷貝陣列 -
string
型別底層是個byte
陣列,也可以進行切片處理。string
是不可變的,如果要修改字串,需要先將字串轉換為切片修改完成後再轉換成為字串。
str := "Hello World." arr := []byte(str) arr[11] = '!' str = string(arr) fmt.Println(str)
(28)Map對映
- Map是一種鍵值對資料結構,宣告方式如下:
var Map名稱 map[KeyType]ValueType
-
使用
make(map[KeyType]ValueType)
分配空間 -
delete(m map[Type]Type, key Type)
:通過Key刪除元素,如果元素不存在也不會報錯 -
清空Map一種是遍歷刪除,一種是
make
重新分配空間,使得原來的Map成為垃圾讓GC回收 -
查詢使用
value, ok = mapName[Key]
,如果ok
為true
,表示元素存在 - Map支援
for-range
遍歷
for key, value := range mapName{ }
- Map支援切片
-
(28)OOP
-
Go中的OOP是通過
struct
來實現的type 類名 struct { 屬性名 資料型別 ... }
-
建立結構體變數
var 變數名稱 結構體型別 var 變數名稱 結構體型別 = 結構體型別{} 變數名稱 := 結構體型別{} // 下面兩種寫法等價: var 變數名稱 *結構體名稱 = new(結構體名稱) var 變數名稱 *結構體名稱 = &結構體名稱 // 在操作屬性、方法的時候Go進行了優化,下面兩種寫法是等價的: (*變數名稱).屬性 = 值 變數名稱.屬性 = 值
-
每一個欄位可以加上一個tag,該tag可以通過反射機制獲取,常見的場景就是序列化與反序列化
屬性名稱 資料型別 `json:Tag名稱`
-
Go中的類沒有建構函式,通常通過工廠模式來實現
package model // 如果Name和Age改為name和age,需要為person繫結Getter和Setter方法 type person struct { Name string `json:"name"` Age int `json:"age"` } func NewPerson(n string, a int)(*person){ return &person{ Name : n, Age : a, } }
package main import "fmt" import "StudyGo/model" func main() { var tom = model.NewPerson("Tom", 20) fmt.Println((*tom).Name) fmt.Println((*tom).Age) }
-
在Go中在一個結構體中巢狀另一個匿名結構體就認為實現了繼承
type Ani struct { name string age int } type Cat struct { Ani say string }
結構體變數.資料型別 = 值
-
介面
type 介面名稱 interface { 方法名稱(引數列表)(返回值列表) }
- 介面不允許包含任何變數
- Go中的介面不需要顯式實現,只要一個變數含有介面的所有方法,那麼就實現了這個介面
-
型別斷言
- 當一個型別轉換為了介面型別在轉換為該型別時需要使用型別斷言判斷是否可以轉換為該型別
var number float32 var x interface{} x = t t = x.(float32) // 判斷一下是否可以轉換成為float32型別
(29)方法
func (recv type) funcName (paramsList)(returnList) { // something }
-
recv
表示這個方法與type
類進行繫結,方法內通過recv
操作type
類中的欄位 -
type
是個結構體型別 -
recv
是個結構體型別變數
通常為了執行效率一般不會直接傳入結構體型別作為接收器,而是結構體型別指標:
func (dog *Dog) function()(){ // 繫結的是地址,操作時也要使用地址 // something } // 呼叫時 var d Dog (&d).function()
但是編譯器做出了相關的優化:
var d Dog d.function()