寫給大忙人看的Go語言(一)
寫給大忙人看的Golang教程(一)
閱讀本文之前,我認為你已經掌握其他語言基礎並寫出一個簡單的項目。
(1)Golang編程註意事項
- 源文件必須以
.go
為擴展名. - Go應用程序d額執行入口是
main()
方法. - Go代碼嚴格區分大小寫.
- Go代碼不需要分號.
- Go代碼不允許多條語句在同一行出現.
- Go語言重定義的變量和導入的包如果沒有被使用不會編譯通過.
- Go語言大括號是成對出現的.
(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的官方網站 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中的標識符
- Go中使用
_
表示空標識符 - 嚴格區分大小寫
- 包名盡量與目錄保持一致
- 推薦使用駝峰命名法
- 變量名稱、函數名稱、常量名稱首字母大寫表示可以被其他包訪問,否則表示私有的
(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
:將指定的子字符串替換為其他字符串,n代表替換個數,-1表示全部,返回新字符串string.ToLower(s string)
:將字符串轉換為小寫string.ToUpper(s string)
:將字符串轉換為大寫string.Split(s string, sep string) array
:將字符串按照sep分隔string.TrimSpace(s string) string
:刪除字符串兩側的空格string.Trim(s string, sub string) string
:將字符串兩側的sub去掉string.TrimLeft(s string, sub string) string
:將字符串左邊的sub刪除string.TrimRight(s string, sub string) string
:將字符串右邊的sub刪除string.HasPrefix(s string, sub string) bool
:判斷s是否以sub開頭string.HasSuffix(s string, sub string) bool
:判斷s是否以sub結尾
(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 }
- Go中的結構體可以訪問嵌套結構體中的所有字段和方法
- 結構體的字段和屬性采用就近原則
- 如果一個結構體繼承自兩個結構體,這兩個結構體都有同名字段但是該子結構體沒有,訪問時需要指明父結構體
- struct支持匿名字段,但是數據類型不能重復,使用
結構體變量.數據類型 = 值
來訪問
-
接口
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()
寫給大忙人看的Go語言(一)