Go 語(yǔ)言入門指南: 環(huán)境搭建、基礎(chǔ)語(yǔ)法和常用特性解析 | 青訓(xùn)營(yíng)
從零開始
Go 語(yǔ)言簡(jiǎn)介
Go 是一個(gè)開源的編程語(yǔ)言,它能讓構(gòu)造簡(jiǎn)單、可靠且高效的軟件變得容易。
Go是從2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持開發(fā),后來(lái)還加入了Ian Lance Taylor, Russ Cox等人,并最終于2009年11月開源,在2012年早些時(shí)候發(fā)布了Go 1穩(wěn)定版本。現(xiàn)在Go的開發(fā)已經(jīng)是完全開放的,并且擁有一個(gè)活躍的社區(qū)。
Go語(yǔ)言的設(shè)計(jì)目標(biāo)是簡(jiǎn)單、高效、可靠,并且易于編程,以解決其他編程語(yǔ)言的一些痛點(diǎn)和限制。
- 并發(fā)支持:Go 語(yǔ)言天生支持并發(fā)編程,提供了輕量級(jí)的Goroutines和通道(Channel)機(jī)制,使并發(fā)編程變得簡(jiǎn)單和高效。這使得編寫并發(fā)程序變得容易,且能夠充分利用多核處理器。
- 高性能:Go 語(yǔ)言在運(yùn)行時(shí)性能方面表現(xiàn)優(yōu)異。其編譯器能夠產(chǎn)生高效的機(jī)器碼,使得 Go 程序在性能上能夠與 C++ 等編程語(yǔ)言相媲美。
- 跨平臺(tái):Go語(yǔ)言支持跨多個(gè)平臺(tái),包括Windows、Linux、macOS等。開發(fā)者可以方便地編譯和運(yùn)行代碼在不同的操作系統(tǒng)上。
- 強(qiáng)調(diào)工具鏈:Go語(yǔ)言提供了完善的工具鏈,包括代碼格式化工具、測(cè)試工具、性能分析工具等,使得開發(fā)、測(cè)試和部署變得更加簡(jiǎn)單和高效。
- 豐富的標(biāo)準(zhǔn)庫(kù):Go語(yǔ)言內(nèi)置了許多常用的庫(kù),涵蓋網(wǎng)絡(luò)、文件操作、加密、文本處理等多個(gè)領(lǐng)域,讓開發(fā)者能夠更輕松地構(gòu)建復(fù)雜的應(yīng)用程序。
總體而言,Go語(yǔ)言的背景和特色使其成為一門優(yōu)秀的現(xiàn)代編程語(yǔ)言,適用于構(gòu)建高性能、高并發(fā)的系統(tǒng),也適合快速開發(fā)和輕松上手。它在云計(jì)算、網(wǎng)絡(luò)服務(wù)、分布式系統(tǒng)等領(lǐng)域得到廣泛應(yīng)用,并持續(xù)在開發(fā)者社區(qū)中受到關(guān)注和支持。
開發(fā)環(huán)境搭建
本文將以 Windows 下的 VS Code 作為例子介紹 Go 開發(fā)環(huán)境的搭建方法。當(dāng)然你也可以使用 Goland 作為你的 IDE。
-
安裝Go語(yǔ)言
首先,你需要下載并安裝Go語(yǔ)言的二進(jìn)制發(fā)行版本。前往官網(wǎng)下載適用于Windows的安裝程序。
運(yùn)行安裝向?qū)?,沒啥問(wèn)題的話一路下一步即可。
-
配置環(huán)境變量
正確安裝完應(yīng)該都會(huì)有,只是確認(rèn)一下。
確保 Go 語(yǔ)言的安裝目錄已經(jīng)添加到系統(tǒng)的環(huán)境變量中。在開始菜單中搜索
path
,打開編輯系統(tǒng)環(huán)境變量
對(duì)話框,在系統(tǒng)變量
中找到名為 “Path” 的變量,點(diǎn)擊“編輯”,然后添加Go語(yǔ)言的安裝路徑(默認(rèn)為C:\Program Files\Go\bin
或你自定義的路徑)。 -
安裝 Visual Studio Code
-
安裝 Go 擴(kuò)展
在 VS Code 中,打開擴(kuò)展視圖(快捷鍵
Ctrl+Shift+X
),搜索并安裝 Go 的官方擴(kuò)展。安裝完畢后,重新啟動(dòng) VS Code。
-
配置 Go Proxy
由于總所周知的原因,國(guó)內(nèi)網(wǎng)絡(luò)環(huán)境下載各種包的速度很慢很慢。因此,我們需要配置一下 Go proxy。
訪問(wèn) GOPROXY.IO - 一個(gè)全球代理 為 Go 模塊而生 按照說(shuō)明設(shè)置你的開發(fā)環(huán)境。
使配置長(zhǎng)久生效 (推薦)
上面的配置步驟只會(huì)當(dāng)次終端內(nèi)生效,如何長(zhǎng)久生效呢,這樣就不用每次都去配置環(huán)境變量了。
Mac/Linux
# 設(shè)置你的 bash 環(huán)境變量 echo "export GOPROXY=https://proxy.golang.com.cn,direct" >> ~/.profile && source ~/.profile # 如果你的終端是 zsh,使用以下命令 echo "export GOPROXY=https://proxy.golang.com.cn,direct" >> ~/.zshrc && source ~/.zshrc
Windows
1. 右鍵 我的電腦 -> 屬性 -> 高級(jí)系統(tǒng)設(shè)置 -> 環(huán)境變量 2. 在 “[你的用戶名]的用戶變量” 中點(diǎn)擊 ”新建“ 按鈕 3. 在 “變量名” 輸入框并新增 “GOPROXY” 4. 在對(duì)應(yīng)的 “變量值” 輸入框中新增 “https://proxy.golang.com.cn,direct” 5. 最后點(diǎn)擊 “確定” 按鈕保存設(shè)置
-
配置 Go 環(huán)境變量(可選)
打開 VS Code后,按
Ctrl+Shift+P
,輸入Go: Install/Update Tools
,選擇并運(yùn)行該命令。它將會(huì)為你安裝Go語(yǔ)言的一些常用工具,如格式化工具(gofmt)、代碼檢查工具(golint)、調(diào)試器(delve)等。 -
創(chuàng)建 Go 項(xiàng)目
現(xiàn)在,你可以創(chuàng)建一個(gè)Go項(xiàng)目并開始編寫代碼了。在任意目錄下創(chuàng)建你的項(xiàng)目文件夾,然后在 VS Code中打開該文件夾。VS Code 會(huì)自動(dòng)識(shí)別 Go 語(yǔ)言項(xiàng)目,并在左下角顯示“Go”標(biāo)志。
-
Hello, world!
現(xiàn)在你已經(jīng)搭建好了Go語(yǔ)言的開發(fā)環(huán)境,可以在 VS Code 中創(chuàng)建和編輯 Go 源代碼文件,并使用Go的工具鏈進(jìn)行編譯、運(yùn)行和調(diào)試。
試試 Hello, world 吧!
package main import "fmt" func main() { ? ? fmt.Println("Hello, World!") }
Let's Go! 基本語(yǔ)法
Hello World
首先,讓我們看看 Hello world 程序中發(fā)生了什么?
package main
import "fmt"
// This is discription
func main() {
? ? fmt.Println("Hello, World!")
}
讓我們來(lái)看下以上程序的各個(gè)部分:
-
Package聲明
代碼以
package main
開頭,表明這個(gè)Go文件屬于main
包。在Go中,每個(gè)可執(zhí)行程序都必須有一個(gè)main
包,并且在main
包中必須有一個(gè)main()
函數(shù)作為程序的入口點(diǎn)。 -
導(dǎo)入語(yǔ)句
使用
import "fmt"
導(dǎo)入了fmt
包。fmt
包是Go標(biāo)準(zhǔn)庫(kù)中的一個(gè)包,提供了輸入輸出和格式化文本的功能。在本代碼中,使用了fmt.Println()
函數(shù)來(lái)輸出文本。 -
注釋
// This is description
是一行注釋。在Go中,使用//
表示單行注釋,用于向代碼中添加解釋和說(shuō)明。 -
main函數(shù)
在Go程序中,
main()
函數(shù)是程序的入口點(diǎn)。當(dāng)程序執(zhí)行時(shí),它會(huì)首先執(zhí)行main()
函數(shù)。在這段代碼中,main()
函數(shù)簡(jiǎn)單地調(diào)用fmt.Println()
函數(shù),將字符串"Hello, World!"輸出到控制臺(tái)。 -
執(zhí)行輸出
代碼通過(guò)調(diào)用
fmt.Println("Hello, World!")
將 "Hello, World!" 這個(gè)字符串輸出到終端。fmt.Println()
函數(shù)是一個(gè)方便的方法,用于在控制臺(tái)輸出文本,并在輸出的最后自動(dòng)添加一個(gè)換行符。
注意:在Go中,函數(shù)名首字母的大小寫決定了該函數(shù)的可見性。
如果函數(shù)名以大寫字母開頭,那么它是可導(dǎo)出的 ,其他包可以訪問(wèn)該函數(shù)。(類似面向?qū)ο笳Z(yǔ)言中的 public)
如果函數(shù)名以小寫字母開頭,它是私有的,其他包無(wú)法訪問(wèn)該函數(shù)。(類似面向?qū)ο笳Z(yǔ)言中的 protected )
在這個(gè)例子中,main()
函數(shù)是可導(dǎo)出的,因?yàn)樗源髮懽帜搁_頭,允許被其他包調(diào)用。
變量和常量
在Go中,變量和常量的聲明有一些不同。以下是它們的聲明方式和特點(diǎn)
(參考 Go's Declaration Syntax - Go 語(yǔ)言博客 (go-zh.org)了解這種類型聲明形式出現(xiàn)的原因。)
變量聲明:
- 使用關(guān)鍵字
var
來(lái)聲明一個(gè)變量,語(yǔ)法為:var variableName dataType
- 變量名必須以字母或下劃線開頭,可以包含字母、數(shù)字和下劃線,但不能使用Go的關(guān)鍵字。
- 可以一次性聲明多個(gè)變量,例如:
var x, y int
- 如果聲明時(shí)未初始化變量,則變量會(huì)被賦予其數(shù)據(jù)類型的零值,比如:
int
類型的零值是0
,string
類型的零值是空字符串""
。 - 可以使用短變量聲明來(lái)創(chuàng)建并初始化變量,語(yǔ)法為:
variableName := value
示例:
var age int // 聲明一個(gè)整數(shù)變量 age
var name string // 聲明一個(gè)字符串變量 name
var x, y int // 聲明兩個(gè)整數(shù)變量 x, y
x = 10 // 給 x 賦值 10
name = "John" // 給 name 賦值 "John"
var count = 5 // 聲明一個(gè)整數(shù)變量 count 并初始化為 5
email := "example@go.com" // 使用短變量聲明聲明一個(gè)字符串變量 email 并初始化為 "example@go.com"
在Go中,變量聲明后必須使用,否則會(huì)導(dǎo)致編譯錯(cuò)誤。
Tips: 如果想要聲明一個(gè)變量但不使用它,可以使用下劃線
_
來(lái)代替變量名,表示該變量被丟棄,不會(huì)占用內(nèi)存空間。例如:_ = 100 // 聲明一個(gè)未使用的變量并初始化為 100,但在后續(xù)代碼中并不使用該變量
總結(jié):Go中的變量聲明可以使用
var
關(guān)鍵字或短變量聲明。變量聲明時(shí)可以指定數(shù)據(jù)類型和初始值。未使用的變量可以使用下劃線_
來(lái)代替。
常量聲明:
- 使用關(guān)鍵字
const
來(lái)聲明常量,語(yǔ)法為:const constantName dataType = value
- 常量的值在聲明時(shí)必須初始化,并且一旦賦值不能再修改。
- 常量可以是字符、字符串、布爾值或數(shù)值類型(整數(shù)、浮點(diǎn)數(shù))。
- 常量名的命名規(guī)則與變量相同,以字母或下劃線開頭,可以包含字母、數(shù)字和下劃線,但不能使用Go的關(guān)鍵字。
- 常量的值必須是一個(gè)編譯時(shí)可以確定的表達(dá)式,例如:
const PI = 3.14
示例:
const PI = 3.14 // 聲明一個(gè)名為 PI 的常量并賦值為 3.14
const appName = "MyApp" // 聲明一個(gè)名為 appName 的常量并賦值為 "MyApp"
const isDebug = true // 聲明一個(gè)名為 isDebug 的常量并賦值為 true
const (
monday = "Monday"
tuesday = "Tuesday"
// 可以在常量組中一次性聲明多個(gè)常量
)
常量必須在聲明時(shí)初始化,并且其值在程序運(yùn)行期間不能改變。
數(shù)據(jù)類型
在 Go 編程語(yǔ)言中,數(shù)據(jù)類型用于聲明函數(shù)和變量。
數(shù)據(jù)類型的出現(xiàn)是為了把數(shù)據(jù)分成所需內(nèi)存大小不同的數(shù)據(jù),編程的時(shí)候需要用大數(shù)據(jù)的時(shí)候才需要申請(qǐng)大內(nèi)存,就可以充分利用內(nèi)存。
Go 語(yǔ)言按類別有以下幾種數(shù)據(jù)類型:
序號(hào) | 類型和描述 |
---|---|
1 | 布爾型 布爾型的值只可以是常量 true 或者 false。一個(gè)簡(jiǎn)單的例子:var b bool = true。 |
2 | 數(shù)字類型 整型 int 和浮點(diǎn)型 float32、float64,Go 語(yǔ)言支持整型和浮點(diǎn)型數(shù)字,并且支持復(fù)數(shù),其中位的運(yùn)算采用補(bǔ)碼。 |
3 | 字符串類型: 字符串就是一串固定長(zhǎng)度的字符連接起來(lái)的字符序列。Go 的字符串是由單個(gè)字節(jié)連接起來(lái)的。Go 語(yǔ)言的字符串的字節(jié)使用 UTF-8 編碼標(biāo)識(shí) Unicode 文本。 |
4 |
派生類型: 包括: (a) 指針類型(Pointer) (b) 數(shù)組類型 (c) 結(jié)構(gòu)化類型(struct) (d) Channel 類型 (e) 函數(shù)類型 (f) 切片類型 (g) 接口類型(interface) (h) Map 類型 |
復(fù)合數(shù)據(jù)類型
數(shù)組和切片 (Array & Slice)
數(shù)組是一個(gè)固定大小的數(shù)據(jù)結(jié)構(gòu),它包含一組相同類型的元素。在 Go 語(yǔ)言中,創(chuàng)建數(shù)組時(shí),需要指定數(shù)組的大小,并且該大小在聲明后無(wú)法改變。數(shù)組的大小是其類型的一部分,因此類型為 [size]dataType
,其中 size
表示數(shù)組大小, dataType
表示數(shù)組元素的數(shù)據(jù)類型。
- 數(shù)組是值類型,當(dāng)將一個(gè)數(shù)組賦值給另一個(gè)數(shù)組時(shí),會(huì)復(fù)制所有的元素。這意味著對(duì)于大型數(shù)組,復(fù)制操作可能會(huì)比較耗時(shí)和內(nèi)存。
// 聲明一個(gè)包含5個(gè)整數(shù)的數(shù)組
var numbers [5]int
// 初始化數(shù)組元素
numbers = [5]int{1, 2, 3, 4, 5}
// 聲明并初始化一個(gè)數(shù)組
numbers := [5]int{1, 2, 3, 4, 5}
切片是對(duì)數(shù)組的引用。切片不需要指定大小,可以動(dòng)態(tài)增長(zhǎng)和收縮。在聲明切片時(shí),不需要指定大小,只需要指定元素的類型。
對(duì)切片的修改會(huì)影響到底層數(shù)組。當(dāng)將一個(gè)切片賦值給另一個(gè)切片時(shí),它們會(huì)引用相同的底層數(shù)組。
// 聲明一個(gè)切片
var mySlice []int
// 使用 make() 函數(shù)創(chuàng)建切片,第一個(gè)參數(shù)是切片類型,第二個(gè)參數(shù)是切片長(zhǎng)度,第三個(gè)參數(shù)是切片容量(可選)
mySlice = make([]int, 5) // 長(zhǎng)度為5,容量也為5的切片
// 初始化一個(gè)切片
mySlice = []int{1, 2, 3, 4, 5}
// 切片的動(dòng)態(tài)增長(zhǎng)
mySlice = append(mySlice, 6, 7, 8) // 添加元素到切片末尾
需要注意的是,切片本身并不存儲(chǔ)數(shù)據(jù),它只是一個(gè)引用,指向底層數(shù)組的一部分。當(dāng)切片的容量不足時(shí),底層數(shù)組會(huì)被自動(dòng)擴(kuò)容。
你可以創(chuàng)建一個(gè)新的切片,它引用了現(xiàn)有切片或數(shù)組的一部分。這被稱為“切片的切片”。切片的切片的語(yǔ)法是 slice[start:end]
,其中 start
是第一個(gè)元素的索引(包括),end
是最后一個(gè)元素后面的索引(不包括)。結(jié)果切片的長(zhǎng)度是 end - start
,容量是從 start
索引到底層數(shù)組末尾的元素?cái)?shù)量。
// 切片的切片,獲取子切片
subSlice := mySlice[2:5] // 這將創(chuàng)建一個(gè)從索引 2 到索引 4(不包括 5)的子切片
映射(Map)
在Go中,映射(Map)是一種鍵值對(duì)的無(wú)序集合,類似于Python中的字典(dict)。它提供了一種方便的方式來(lái)存儲(chǔ)和檢索鍵值對(duì),并且允許根據(jù)鍵快速查找對(duì)應(yīng)的值。
創(chuàng)建映射(Map)
在Go中,使用map[keyType]valueType
的語(yǔ)法來(lái)聲明一個(gè)映射。其中keyType
表示鍵的數(shù)據(jù)類型,valueType
表示值的數(shù)據(jù)類型。
// 聲明一個(gè)映射,鍵為字符串類型,值為整數(shù)類型
var myMap map[string]int
初始化映射
使用make()
函數(shù)來(lái)初始化一個(gè)映射。初始化映射后,才能對(duì)其進(jìn)行賦值和操作。
// 初始化一個(gè)映射
myMap := make(map[string]int)
添加和更新鍵值對(duì)
可以使用賦值操作符(=
)來(lái)添加或更新映射中的鍵值對(duì)。如果指定的鍵不存在,它將被添加到映射中;如果指定的鍵已經(jīng)存在,它將更新對(duì)應(yīng)的值。
myMap["apple"] = 10
myMap["banana"] = 5
myMap["apple"] = 15 // 更新鍵 "apple" 對(duì)應(yīng)的值
訪問(wèn)和檢查鍵值對(duì)
可以通過(guò)指定鍵來(lái)訪問(wèn)映射中的值。如果指定的鍵不存在,將返回值類型的零值。為了區(qū)分鍵不存在和值為零值兩種情況,可以使用多返回值的方式來(lái)檢查鍵是否存在。
value := myMap["apple"] // 訪問(wèn)鍵 "apple" 對(duì)應(yīng)的值
// 檢查鍵是否存在
value, exists := myMap["orange"]
if exists {
fmt.Println("The value for 'orange' is:", value)
} else {
fmt.Println("Key 'orange' does not exist.")
}
刪除鍵值對(duì)
可以使用delete()
函數(shù)來(lái)刪除映射中的鍵值對(duì)。如果指定的鍵不存在,delete()
函數(shù)不會(huì)產(chǎn)生錯(cuò)誤。
delete(myMap, "banana") // 刪除鍵 "banana" 對(duì)應(yīng)的鍵值對(duì)
遍歷映射
使用for range
循環(huán)可以遍歷映射中的所有鍵值對(duì)。
for key, value := range myMap {
fmt.Println(key, value)
}
Go 中的映射是一個(gè)強(qiáng)大且方便的數(shù)據(jù)結(jié)構(gòu),它在許多場(chǎng)景下都非常有用,特別是用于表示鍵值對(duì)的集合。與 Python 中的字典類似,Go 的映射提供了快速的鍵值查找和更新,是處理鍵值對(duì)數(shù)據(jù)的理想選擇。
結(jié)構(gòu)體(Struct)
在Go語(yǔ)言中,結(jié)構(gòu)體(Struct)是一種自定義的復(fù)合數(shù)據(jù)類型,它允許我們將不同類型的數(shù)據(jù)組合在一起,形成一個(gè)新的數(shù)據(jù)結(jié)構(gòu)。結(jié)構(gòu)體是由一系列字段(fields)組成,每個(gè)字段可以是不同的數(shù)據(jù)類型。結(jié)構(gòu)體中的字段稱為成員(members),它們表示結(jié)構(gòu)體的特征和屬性。
聲明結(jié)構(gòu)體
使用type
關(guān)鍵字來(lái)聲明一個(gè)新的結(jié)構(gòu)體類型。結(jié)構(gòu)體的定義以關(guān)鍵字type
開頭,后面緊跟結(jié)構(gòu)體的名稱,然后是一個(gè)由字段組成的花括號(hào)代碼塊。
type Person struct {
Name string
Age int
Height float64
}
創(chuàng)建結(jié)構(gòu)體實(shí)例
通過(guò)聲明一個(gè)結(jié)構(gòu)體變量并為其成員賦值,我們可以創(chuàng)建結(jié)構(gòu)體的實(shí)例。
// 創(chuàng)建一個(gè)Person結(jié)構(gòu)體的實(shí)例
person1 := Person{
Name: "Alice",
Age: 30,
Height: 1.75,
}
訪問(wèn)結(jié)構(gòu)體成員
可以使用.
運(yùn)算符來(lái)訪問(wèn)結(jié)構(gòu)體的成員。
fmt.Println(person1.Name) // 輸出: "Alice"
fmt.Println(person1.Age) // 輸出: 30
fmt.Println(person1.Height) // 輸出: 1.75
結(jié)構(gòu)體的匿名字段
結(jié)構(gòu)體允許字段沒有名稱,這樣的字段稱為匿名字段。匿名字段的數(shù)據(jù)類型必須是命名的類型或具有命名的類型。
type Circle struct {
float64 // 匿名字段,代表圓的半徑
}
嵌套結(jié)構(gòu)體
結(jié)構(gòu)體可以包含其他結(jié)構(gòu)體作為其成員,這被稱為嵌套結(jié)構(gòu)體。通過(guò)嵌套結(jié)構(gòu)體,我們可以建立更復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。
type Address struct {
City string
State string
}
type Person struct {
Name string
Age int
Address Address // 嵌套結(jié)構(gòu)體作為成員
}
結(jié)構(gòu)體的方法
結(jié)構(gòu)體可以關(guān)聯(lián)方法,通過(guò)這些方法可以為結(jié)構(gòu)體類型添加行為。方法是特殊類型的函數(shù),它們與結(jié)構(gòu)體關(guān)聯(lián),可以在結(jié)構(gòu)體實(shí)例上調(diào)用。
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
rect := Rectangle{Width: 10, Height: 5}
area := rect.Area() // 調(diào)用結(jié)構(gòu)體的方法
在青訓(xùn)營(yíng)課程中,使用了結(jié)構(gòu)體來(lái)格式化 Request 和 Response 數(shù)據(jù)。
接口(Interface)
在Go語(yǔ)言中,接口(Interface)是一種抽象類型,它定義了一組方法的集合,但并不提供這些方法的具體實(shí)現(xiàn)。接口描述了對(duì)象的行為,而不關(guān)心對(duì)象的具體類型。任何類型只要實(shí)現(xiàn)了接口中定義的所有方法,就被認(rèn)為是實(shí)現(xiàn)了該接口。
定義接口
使用type
關(guān)鍵字和interface
關(guān)鍵字來(lái)定義一個(gè)接口。接口的定義由一組方法簽名組成,這些方法簽名描述了接口中的方法。方法簽名包括方法的名稱、參數(shù)列表和返回值列表。
type Shape interface {
Area() float64
Perimeter() float64
}
實(shí)現(xiàn)接口
要實(shí)現(xiàn)一個(gè)接口,只需在類型上提供接口中定義的所有方法。實(shí)現(xiàn)接口不需要顯式地聲明實(shí)現(xiàn)了哪個(gè)接口,只要提供了接口中的方法,編譯器會(huì)自動(dòng)判斷該類型實(shí)現(xiàn)了哪些接口。
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2*r.Width + 2*r.Height
}
多態(tài)性
接口的一個(gè)重要特性是多態(tài)性。多態(tài)性使得一個(gè)接口類型的變量可以在不同實(shí)現(xiàn)類型上調(diào)用相同的方法,實(shí)現(xiàn)了面向?qū)ο缶幊讨械亩鄳B(tài)概念。
func printShapeInfo(s Shape) {
fmt.Println("Area:", s.Area())
fmt.Println("Perimeter:", s.Perimeter())
}
rect := Rectangle{Width: 5, Height: 3}
printShapeInfo(rect)
在上面的示例中,我們定義了一個(gè)Shape
接口,它有兩個(gè)方法Area()
和Perimeter()
。然后我們創(chuàng)建了一個(gè)Rectangle
類型并為它實(shí)現(xiàn)了Shape
接口的方法。在printShapeInfo
函數(shù)中,我們接收一個(gè)Shape
接口類型的參數(shù),并在不同類型的實(shí)現(xiàn)上調(diào)用了Area()
和Perimeter()
方法。這樣,printShapeInfo
函數(shù)可以處理任何實(shí)現(xiàn)了Shape
接口的類型,實(shí)現(xiàn)了多態(tài)性。
通過(guò)接口,我們可以編寫更靈活、可擴(kuò)展的代碼,因?yàn)榻涌趯?shí)現(xiàn)細(xì)節(jié)與接口的使用者解耦,允許我們對(duì)代碼進(jìn)行抽象和復(fù)用。
流程控制
條件語(yǔ)句
在Go語(yǔ)言中,條件語(yǔ)句主要包括if
、else
和switch
三種類型。它們用于根據(jù)不同條件執(zhí)行不同的代碼塊。
if 語(yǔ)句
if
語(yǔ)句用于根據(jù)一個(gè)表達(dá)式的結(jié)果來(lái)執(zhí)行相應(yīng)的代碼塊。如果表達(dá)式的值為true
,則執(zhí)行if
后的代碼塊;如果表達(dá)式的值為false
,則跳過(guò)if
后的代碼塊。
num := 10
if num > 0 {
fmt.Println("Positive")
} else if num == 0 {
fmt.Println("Zero")
} else {
fmt.Println("Negative")
}
else
語(yǔ)句用于在if
條件為false
時(shí)執(zhí)行一個(gè)備用的代碼塊。
num := 10
if num%2 == 0 {
fmt.Println("Even")
} else {
fmt.Println("Odd")
}
switch 語(yǔ)句
switch
是編寫一連串 if
-else
語(yǔ)句的簡(jiǎn)便方法。它運(yùn)行第一個(gè)值等于條件表達(dá)式的 case 語(yǔ)句。
Go 的 switch 語(yǔ)句類似于 C、C++、Java、JavaScript 和 PHP 中的,不過(guò) Go 只運(yùn)行選定的 case,而非之后所有的 case。
Go 的另一點(diǎn)重要的不同在于 switch 的 case 無(wú)需為常量,且取值不必為整數(shù)。
day := "Sunday"
switch day {
case "Monday":
fmt.Println("Start of the week")
case "Tuesday":
fmt.Println("Second day")
case "Wednesday", "Thursday":
fmt.Println("Middle of the week")
case "Friday":
fmt.Println("End of the work week")
case "Saturday", "Sunday":
fmt.Println("Weekend")
default:
fmt.Println("Invalid day")
}
實(shí)際上,Go 自動(dòng)提供了在這些語(yǔ)言中每個(gè) case 后面所需的 break
語(yǔ)句。除非以 fallthrough
語(yǔ)句結(jié)束,否則分支會(huì)自動(dòng)終止。
num := 2
switch num {
case 1:
fmt.Println("One")
fallthrough
case 2:
fmt.Println("Two")
case 3:
fmt.Println("Three")
}
在上面的例子中,當(dāng)num
為2時(shí),會(huì)輸出:
Two
Three
這是因?yàn)樵诘谝粋€(gè)case
中使用了fallthrough
關(guān)鍵字,導(dǎo)致繼續(xù)執(zhí)行下一個(gè)case
的代碼塊。
循環(huán)語(yǔ)句
Go 只有一種循環(huán)結(jié)構(gòu):for
循環(huán)。
基本的 for
循環(huán)由三部分組成,它們用分號(hào)隔開:
- 初始化語(yǔ)句:在第一次迭代前執(zhí)行
- 條件表達(dá)式:在每次迭代前求值
- 后置語(yǔ)句:在每次迭代的結(jié)尾執(zhí)行
初始化語(yǔ)句通常為一句短變量聲明,該變量聲明僅在 for
語(yǔ)句的作用域中可見。
一旦條件表達(dá)式的布爾值為 false
,循環(huán)迭代就會(huì)終止。
注意:和 C、Java、JavaScript 之類的語(yǔ)言不同,Go 的 for 語(yǔ)句后面的三個(gè)構(gòu)成部分外沒有小括號(hào),
大括號(hào) {
}` 則是必須的。
- 基本的
for
循環(huán):類似于C語(yǔ)言的for
循環(huán),使用for
關(guān)鍵字和循環(huán)條件。循環(huán)條件可以是一個(gè)布爾表達(dá)式,當(dāng)條件為true
時(shí)循環(huán)會(huì)繼續(xù)執(zhí)行。
for i := 0; i < 5; i++ {
fmt.Println(i)
}
-
for
循環(huán)省略初始化和步進(jìn):在for
循環(huán)中,初始化和步進(jìn)語(yǔ)句都是可選的。如果省略初始化語(yǔ)句,那么相當(dāng)于一個(gè)無(wú)限循環(huán)。
i := 0
for ; i < 5; {
fmt.Println(i)
i++
}
-
for
循環(huán)省略全部條件:可以將for
循環(huán)的條件部分省略,相當(dāng)于一個(gè)無(wú)限循環(huán)。
i := 0
for {
fmt.Println(i)
i++
if i >= 5 {
break
}
}
在循環(huán)中,我們常常會(huì)用到 range
來(lái)遍歷可迭代的數(shù)據(jù)結(jié)構(gòu)。
numbers := []int{1, 2, 3, 4, 5}
for index, value := range numbers {
fmt.Printf("Index: %d, Value: %d\n", index, value)
}
range
循環(huán)會(huì)返回兩個(gè)值,一個(gè)是索引(或鍵),另一個(gè)是對(duì)應(yīng)索引(或鍵)的元素值??梢允褂每瞻讟?biāo)識(shí)符_
來(lái)忽略其中的某個(gè)值。
for _, value := range numbers {
fmt.Println(value)
}
range
循環(huán)也可以用于遍歷映射的鍵值對(duì)。
mymap := map[string]int{"apple": 1, "banana": 2, "orange": 3}
for key, value := range mymap {
fmt.Printf("Key: %s, Value: %d\n", key, value)
}
在for
循環(huán)和range
循環(huán)中,我們可以使用break
和continue
關(guān)鍵字來(lái)控制循環(huán)的執(zhí)行流程,實(shí)現(xiàn)各種不同的邏輯。
函數(shù)
函數(shù)的聲明和定義
使用func
關(guān)鍵字來(lái)聲明和定義一個(gè)函數(shù)。函數(shù)的聲明包括函數(shù)名、參數(shù)列表和返回值。如果函數(shù)沒有返回值,則可以省略返回值的部分。
func add(a, b int) int {
return a + b
}
func greet(name string) {
fmt.Println("Hello, " + name)
}
調(diào)用函數(shù)
要調(diào)用一個(gè)函數(shù),只需要寫出函數(shù)名并傳遞必要的參數(shù)。如果函數(shù)有返回值,可以使用變量接收返回值。
result := add(5, 3)
greet("Alice")
多返回值
Go語(yǔ)言中的函數(shù)可以返回多個(gè)值。多返回值在很多場(chǎng)景下非常有用,例如可以返回結(jié)果和錯(cuò)誤,或者返回多個(gè)計(jì)算結(jié)果。
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
匿名函數(shù)
在Go中,可以創(chuàng)建匿名函數(shù)(沒有名字的函數(shù)),并將其分配給變量。匿名函數(shù)通常用于簡(jiǎn)短的代碼塊,例如作為函數(shù)參數(shù)傳遞或在閉包中使用。
add := func(a, b int) int {
return a + b
}
result := add(3, 5)
變長(zhǎng)參數(shù)
Go語(yǔ)言支持變長(zhǎng)參數(shù)(可變參數(shù)),通過(guò)在參數(shù)類型前加上三個(gè)點(diǎn)(...
)來(lái)指示。這允許函數(shù)接受任意數(shù)量的參數(shù)。
func sum(numbers ...int) int {
total := 0
for _, num := range numbers {
total += num
}
return total
}
result := sum(1, 2, 3, 4, 5) // result = 15
函數(shù)作為參數(shù)和返回值
在Go中,函數(shù)可以作為參數(shù)傳遞給其他函數(shù),也可以作為函數(shù)的返回值。
func add(a, b int) int {
return a + b
}
func sub(a, b int) int {
return a - b
}
func calculate(op func(int, int) int, a, b int) int {
return op(a, b)
}
result1 := calculate(add, 5, 3) // result1 = 8
result2 := calculate(sub, 5, 3) // result2 = 2
錯(cuò)誤處理
Go語(yǔ)言中的錯(cuò)誤處理是一種機(jī)制,用于處理函數(shù)執(zhí)行中可能發(fā)生的錯(cuò)誤情況。在Go中,錯(cuò)誤是一個(gè)接口類型(error
),它通常用于表示函數(shù)是否執(zhí)行成功以及錯(cuò)誤的具體信息。
錯(cuò)誤的定義
在Go中,error
接口是一個(gè)預(yù)定義的接口,它只有一個(gè)方法:
type error interface {
Error() string
}
一個(gè)實(shí)現(xiàn)了error
接口的類型可以表示一個(gè)錯(cuò)誤。通常情況下,返回error
類型的值表示函數(shù)執(zhí)行失敗,返回nil
表示函數(shù)執(zhí)行成功。
函數(shù)返回錯(cuò)誤
在函數(shù)執(zhí)行過(guò)程中,如果發(fā)生錯(cuò)誤,可以通過(guò)返回error
類型的值來(lái)表示錯(cuò)誤。函數(shù)可以返回一個(gè)非nil
的error
值,用于指示函數(shù)執(zhí)行失敗,并攜帶錯(cuò)誤信息。
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
處理錯(cuò)誤
在調(diào)用可能返回錯(cuò)誤的函數(shù)時(shí),通常需要檢查錯(cuò)誤并根據(jù)不同的錯(cuò)誤情況采取相應(yīng)的處理措施。
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
自定義錯(cuò)誤
除了使用errors.New()
函數(shù)創(chuàng)建基本的錯(cuò)誤信息,我們還可以定義自己的錯(cuò)誤類型,只需要實(shí)現(xiàn)error
接口的Error()
方法即可。
type MyError struct {
message string
}
func (e *MyError) Error() string {
return e.message
}
func doSomething() error {
return &MyError{"Something went wrong"}
}
處理多個(gè)錯(cuò)誤
在實(shí)際編程中,一個(gè)函數(shù)可能會(huì)返回多個(gè)錯(cuò)誤,通常使用多個(gè)if
語(yǔ)句或switch
語(yǔ)句來(lái)處理不同的錯(cuò)誤情況。
result1, err1 := doSomething()
result2, err2 := doAnotherThing()
if err1 != nil {
fmt.Println("Error 1:", err1)
}
if err2 != nil {
fmt.Println("Error 2:", err2)
}
// 繼續(xù)處理其他結(jié)果
常用特性解析
Defer 語(yǔ)句
在Go語(yǔ)言中,defer
語(yǔ)句用于在函數(shù)返回之前執(zhí)行一些操作。這些操作可能包括資源釋放、文件關(guān)閉、鎖解鎖等。defer
語(yǔ)句通常用于確保某些操作在函數(shù)返回前一定會(huì)被執(zhí)行,不管函數(shù)是否發(fā)生了錯(cuò)誤或提前返回。
使用defer
關(guān)鍵字,可以在函數(shù)中的任意位置注冊(cè)一個(gè)需要在函數(shù)退出時(shí)執(zhí)行的操作。defer
語(yǔ)句后面跟隨一個(gè)函數(shù)調(diào)用。
func doSomething() {
fmt.Println("Doing something...")
defer fmt.Println("Operation completed.")
}
在上面的例子中,當(dāng)doSomething
函數(shù)執(zhí)行完成時(shí),無(wú)論函數(shù)是正常結(jié)束還是發(fā)生了錯(cuò)誤,Operation completed.
都會(huì)被打印出來(lái)。
defer 的執(zhí)行順序
如果在函數(shù)中有多個(gè)defer
語(yǔ)句,它們的執(zhí)行順序與注冊(cè)的順序相反。即最后注冊(cè)的defer
語(yǔ)句將最先執(zhí)行,最先注冊(cè)的defer
語(yǔ)句將最后執(zhí)行。
func printMessage(msg string) {
defer fmt.Println("Deferred 2")
defer fmt.Println("Deferred 1")
fmt.Println(msg)
}
func main() {
printMessage("Hello, World!")
}
輸出結(jié)果為:
Hello, World!
Deferred 1
Deferred 2
defer語(yǔ)句與參數(shù)的求值:
在注冊(cè)defer
語(yǔ)句時(shí),會(huì)對(duì)其參數(shù)進(jìn)行求值,但實(shí)際執(zhí)行是在函數(shù)返回前。這可能會(huì)導(dǎo)致一些潛在的問(wèn)題,例如當(dāng)參數(shù)是函數(shù)調(diào)用時(shí),可能導(dǎo)致不符合預(yù)期的結(jié)果。
func main() {
x := 0
defer fmt.Println(x) // 輸出 0,因?yàn)榇藭r(shí) x 的值為 0
x++
}
常見用途:
- 關(guān)閉文件:在打開文件后使用
defer
語(yǔ)句關(guān)閉文件,確保文件在函數(shù)返回前被關(guān)閉,從而避免資源泄漏。 - 釋放鎖:在使用鎖時(shí),可以在加鎖后使用
defer
語(yǔ)句來(lái)釋放鎖,確保鎖的正確使用。 - 數(shù)據(jù)庫(kù)連接的關(guān)閉:在使用數(shù)據(jù)庫(kù)連接時(shí),使用
defer
語(yǔ)句關(guān)閉連接,確保連接得到釋放。
總之,defer
語(yǔ)句是Go語(yǔ)言中一種非常有用的機(jī)制,它能夠幫助我們?cè)诤瘮?shù)返回前執(zhí)行一些必要的清理和收尾操作,提高代碼的健壯性和可維護(hù)性。
更多關(guān)于 defer 語(yǔ)句的信息,請(qǐng)閱讀Defer, Panic, and Recover - Go 語(yǔ)言博客 (go-zh.org)
參考資料
Go 語(yǔ)言之旅 (go-zh.org)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-623369.html
Go 語(yǔ)言教程 | 菜鳥教程 (runoob.com)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-623369.html
到了這里,關(guān)于Go 語(yǔ)言入門指南: 環(huán)境搭建、基礎(chǔ)語(yǔ)法和常用特性解析 | 青訓(xùn)營(yíng)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!