圖片:Gopher 吉祥物和舊 logo
簡要介紹下 Go(Golang) 語言。Go 語言是由谷歌工程師 Robert Griesemer、Rob Pike 和 Ken Thompson 創(chuàng)造的一種靜態(tài)類型編譯語言。首個開源版本發(fā)布于2012年3月。
“Go 是一種開源編程語言,可以輕松構(gòu)建簡單、可靠、高效的軟件?!?br>— GoLang
在諸多編程語言中,解決給定問題的方法有很多,程序員往往需要花很多時間思考關(guān)于解決問題的最佳方法。
但是,Go 相信更少的功能 —— 只有一種正確的方法來解決問題。
這節(jié)省了開發(fā)人員的時間,并使大型代碼庫易于維護。 Go 中沒有 maps 和 filters 等“富有表現(xiàn)力”的功能。
“當你需要增加功能的豐富性,往往伴隨著開銷的增加”
- Rob Pike
Golang 最新發(fā)布的 logo: https://blog.golang.org/go-brand
Go 程序由包組成。編譯器會將 main 包編譯成可執(zhí)行程序,而不是共享庫。main 包是整個應(yīng)用的入口。main 包的定義如下:
接下來,咱們在 Go 的工作空間中創(chuàng)建一個叫做 main.go 的文件,來實現(xiàn)一個簡單的 hello world 例子。
Go 的工作空間可以通過環(huán)境變量 GOPATH 定義。
你需要在這個工作空間中編寫你自己的代碼。Go 為搜索 GOPATH 以及 GOROOT 指定的目錄中的所有包,GOROOT 變量是在安裝 GO 時設(shè)置的,也就是 go 語言安裝的目錄。
將 GOPATH 設(shè)置到預(yù)期目錄?,F(xiàn)在,增加一個文件夾 ~/workspace
接下來在這個工作空間中創(chuàng)建 main.go ,寫入下面的代碼。
在上面的示例代碼中,fmt 是 Go 的一個內(nèi)置包,主要實現(xiàn)格式化 I/O 的功能。
在 Go 語言中,通過 import 關(guān)鍵字引入一個包。func main 是可執(zhí)行代碼的入口。fmt 中的 Println 函數(shù)實現(xiàn) “hello world” 的打印。
下面嘗試運行這個文件。我們可以通過兩種方法運行一個 Go 命令。我們知道 Go 是一個編譯性語言,所以,在執(zhí)行之前我們先來編譯它。
上面的命令就生成了一個可執(zhí)行文件 main ,接下來我們運行這個文件:
接下來看另外一種更加簡單的執(zhí)行方式。go run 命令將編譯步驟抽象。那么,通過下面的命令就可以執(zhí)行這個程序。
注意:可以使用 https://play.golang.org 嘗試上面的代碼。
Go 的變量必須顯式聲明。Go 是靜態(tài)類型的語言,也就是說,在變量聲明時要檢查變量類型。可以如下聲明一個變量:
在這種情況下,變量的值會被設(shè)為0。也可以通過下面的語法聲明變量并初始化為不同的值:
在這里,變量自動被判斷為一個整形變量。我們可以通過簡化形式來聲明變量:
也可以在一行聲明多個變量:
和其他編程語言一樣,Go 語言也有不同的數(shù)據(jù)類型。接下來就來看一下:
數(shù)字(Number)、字符串(String)和布爾(Boolean)
可支持數(shù)字存儲類型有 int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr…
字符串以字節(jié)序列的形式存儲。用string關(guān)鍵字表示和聲明。
布爾型變量的關(guān)鍵字是 bool 。
Go 還支持以 complex64 和 complex128 聲明的復(fù)雜數(shù)字類型。
數(shù)組是相同數(shù)據(jù)類型的元素序列。數(shù)組具有在聲明中定義的固定長度,因此不能進行擴展。數(shù)組聲明為:
數(shù)組也可以是多維的。我們可以使用以下格式創(chuàng)建它們:
當數(shù)組的值在運行時更改時,數(shù)組限制了這種情況。 數(shù)組也不提供獲取子數(shù)組的能力。 為此,Go 有一個名為 slices 的數(shù)據(jù)類型。 切片存儲一系列元素,可以隨時擴展。 切片聲明類似于數(shù)組聲明 - 沒有定義容量:
這將創(chuàng)建一個零容量和零長度的切片。切片也可以定義容量和長度。我們可以使用以下語法:
這里,切片的初始長度為5,容量為10。
切片是數(shù)組的抽象。切片使用數(shù)組作為底層結(jié)構(gòu)。切片包含三個組件:容量,長度和指向底層數(shù)組的指針,如下圖所示:
圖片來源: https://blog.golang.org/go-slices-usage-and-internals
通過使用append或copy函數(shù)可以增加切片的容量。 append函數(shù)可以為數(shù)組的末尾添加值,并在需要時增加容量。
增加切片容量的另一種方法是使用復(fù)制功能。只需創(chuàng)建另一個具有更大容量的切片,并將原始切片復(fù)制到新創(chuàng)建的切片:
我們可以創(chuàng)建切片的子切片。這可以使用以下命令完成:
集合是Go中的數(shù)據(jù)類型,它將鍵映射到值。我們可以使用以下命令定義映射:
這里m是新的集合變量,它的鍵為字符串,值為整數(shù)。我們可以輕松地將鍵和值添加到地圖中:
數(shù)據(jù)類型轉(zhuǎn)換
一種數(shù)據(jù)類型可以通過類型轉(zhuǎn)換得到另一種數(shù)據(jù)類型。我們來看一個簡單的例子:
不是所有的數(shù)據(jù)類型都可以轉(zhuǎn)換為別的數(shù)據(jù)類型。必須確保兩種數(shù)據(jù)類型之間的兼容性。
對于條件判斷,我們可以像如下的例子那樣使用 if-else 語句。要確保大括號和條件語句在同一行。
Switch cases 有助于組織多個條件語句。以下示例顯示了一個簡單的 switch case 語句:
Go有一個循環(huán)關(guān)鍵字。 單個for循環(huán)命令有助于實現(xiàn)不同類型的循環(huán):
上面的示例類似于C中的while循環(huán)。對于for循環(huán),也可以使用常見的for語句
Go中的無限循環(huán):
Go提供指針。指針是保存值的地址的地方。指針由*定義。根據(jù)數(shù)據(jù)類型定義指針。例:
這里ap是指向整數(shù)類型的指針。 &運算符可用于獲取變量的地址。
可以使用*運算符訪問指針的值指針:
在將結(jié)構(gòu)作為參數(shù)傳遞時或在為已定義類型聲明方法時,通常首選指針
傳遞值時實際上復(fù)制了值,這意味著更多的內(nèi)存通過指針傳遞
函數(shù)更改的值將反映在方法/函數(shù)調(diào)用者中。
例:
注意:嘗試本文示例代碼時,不要忘記將其包含在main包中,并在需要時導(dǎo)入fmt或其他包,如上面第一個main.go示例所示。
主函數(shù)中定義的主要功能是執(zhí)行程序的入口點,可以定義和使用更多功能。讓我們看一個簡單的例子:
正如我們在上面的例子中所看到的,使用 func 關(guān)鍵字后跟函數(shù)名來定義 Go 函數(shù)。函數(shù)所需的參數(shù)需要根據(jù)其數(shù)據(jù)類型定義,最后是返回的數(shù)據(jù)類型。
函數(shù)的返回也可以在函數(shù)中預(yù)定義:
這里c被定義為返回變量。 因此,定義的變量c將自動返回,而無需在結(jié)尾的return語句中定義。
您還可以從使用逗號分隔返回值的單個函數(shù)返回多個值。
Go不是一個完全面向?qū)ο蟮恼Z言,但是對于結(jié)構(gòu),接口和方法,它有很多面向?qū)ο蟮闹С趾透杏X。
結(jié)構(gòu)是不同字段的類型集合。 結(jié)構(gòu)用于將數(shù)據(jù)分組在一起。 例如,如果我們想要對Person類型的數(shù)據(jù)進行分組,我們會定義一個人的屬性,其中可能包括姓名,年齡,性別。 可以使用以下語法定義結(jié)構(gòu):
在定義了人類型結(jié)構(gòu)的情況下,現(xiàn)在讓我們創(chuàng)建一個人:
我們可以用點(.)輕松訪問這些數(shù)據(jù)
您還可以使用其指針直接訪問結(jié)構(gòu)的屬性
方法是具有接收器的特殊類型的功能。接收器既可以是值,也可以是指針。讓我們創(chuàng)建一個名為describe的方法,它具有我們在上面的例子中創(chuàng)建的接收器類型:
正如我們在上面的例子中所看到的,現(xiàn)在可以使用點運算符作為pp.describe來調(diào)用該方法。 請注意,接收器是指針。 使用指針,我們傳遞對值的引用,因此如果我們對方法進行任何更改,它將反映在接收器pp中。它也不會創(chuàng)建對象的新副本,這樣可以節(jié)省內(nèi)存。
請注意,在上面的示例中,age的值已更改,而name的值未更改,因為方法setName屬于receiver類型,而setAge屬于pointer類型。
Go接口是方法的集合。接口有助于將類型的屬性組合在一起。我們以接口動物為例:
這里的動物是一種接口類型?,F(xiàn)在讓我們創(chuàng)建兩種不同類型的動物來實現(xiàn)動物接口類型:
在main函數(shù)中,我們創(chuàng)建了一個動物類型的變量a。 我們?yōu)閯游锓峙渖吆拓堫愋?,并使用Println打印a.description。 由于我們以不同的方式實現(xiàn)了兩種類型(貓和蛇)中描述的方法,我們可以打印出動物的描述。
我們在Go中編寫所有代碼。主程序包是程序執(zhí)行的入口點。 Go中有很多內(nèi)置包。我們一直使用的最著名的是fmt包。
“在主要機制中使用程序包進行大規(guī)模編程,并且可以將大型項目分成更小的部分?!?- 羅伯特格里塞默
我們安裝的軟件包保存在GOPATH env中,這是我們的工作目錄。您可以通過cd $GOPATH/pkg進入我們的工作目錄中的pkg包管理文件夾。
讓我們開始創(chuàng)建一個自定義包文件目錄:
要創(chuàng)建自定義包,我們需要首先使用我們需要的包名創(chuàng)建一個文件夾。假設(shè)我們正在建立一個包person。為此,我們在custom_package文件夾中創(chuàng)建一個名為person的文件夾:
現(xiàn)在讓我們在這個文件夾中創(chuàng)建一個文件person.go。
我們現(xiàn)在需要安裝包,以便可以導(dǎo)入和使用它。所以讓我們安裝它:
現(xiàn)在讓我們回到custom_package文件夾并創(chuàng)建一個main.go文件
現(xiàn)在,我們可以導(dǎo)入我們創(chuàng)建的包person并使用函數(shù)Description。請注意,我們在包中創(chuàng)建的函數(shù)secretName將無法訪問。在Go中,以大寫字母開頭的方法名稱將是私有的。
包文檔
Go內(nèi)置了對包文檔的支持。運行以下命令以生成文檔:
這將為我們的包person生成Description函數(shù)的文檔。要查看文檔,請使用以下命令運行Web服務(wù)器:
現(xiàn)在轉(zhuǎn)到URL http://localhost:6060/pkg /并查看我們剛剛創(chuàng)建的包的文檔。
Go中的一些內(nèi)置包
fmt
該包實現(xiàn)了格式化的I/O功能。我們已經(jīng)使用該包打印到stdout。
JSON
Go中另一個有用的包是json包。這有助于編碼/解碼JSON。讓我們舉一個例子來編碼/解碼一些json:
編碼
解碼
在使用unmarshal解碼json字節(jié)時,第一個參數(shù)是json字節(jié),第二個參數(shù)是我們希望json映射到的響應(yīng)類型struct的地址。 請注意,json:“page”將頁面鍵映射到struct中的PageNumber鍵。
錯誤是程序的意外和意外結(jié)果。 假設(shè)我們正在對外部服務(wù)進行API調(diào)用。 此API調(diào)用可能成功或可能失敗。 當存在錯誤類型時,可以識別Go程序中的錯誤。 我們來看看這個例子:
這里對錯誤對象的API調(diào)用可能通過或可能失敗。我們可以檢查錯誤是否為nil,并相應(yīng)地處理響應(yīng):
從函數(shù)返回自定義錯誤
當我們編寫自己的函數(shù)時,有些情況下我們會遇到錯誤??梢栽阱e誤對象的幫助下返回這些錯誤:
使用Go構(gòu)建的大多數(shù)軟件包或我們使用的外部軟件包都有一種錯誤處理機制。 所以我們調(diào)用的任何函數(shù)都可能存在錯誤。 這些錯誤永遠不應(yīng)該被忽略,并且總是在我們稱之為函數(shù)的地方優(yōu)雅地處理,就像我們在上面的例子中所做的那樣。
Panic是一種未經(jīng)處理的異常,在程序執(zhí)行期間突然遇到。 在Go中,Panic不是處理程序中異常的理想方式。 建議使用錯誤對象。 發(fā)生Panic時,程序執(zhí)行停止。 Panic之后執(zhí)行的事情就是defer。
Defer總是在函數(shù)結(jié)束時執(zhí)行。
在上面的例子中,我們使用panic()執(zhí)行程序。 正如您所注意到的,有一個defer語句,它將使程序在程序執(zhí)行結(jié)束時執(zhí)行該行。 當我們需要在函數(shù)結(jié)束時執(zhí)行某些操作時,也可以使用Defer,例如關(guān)閉文件。
Go是建立在并發(fā)性的基礎(chǔ)上的。 Go中的并發(fā)可以通過輕量級線程的Go例程來實現(xiàn)。
Go routine是可以與另一個函數(shù)并行或同時運行的函數(shù)。 創(chuàng)建Go routine非常簡單。 只需在函數(shù)前添加關(guān)鍵字Go,我們就可以使它并行執(zhí)行。 Go routine非常輕量級,因此我們可以創(chuàng)建數(shù)千個例程。 讓我們看一個簡單的例子:
正如您在上面的示例中所看到的,函數(shù)c是一個Go routine ,它與主Go線程并行執(zhí)行。 有時我們想要在多個線程之間共享資源。 Go更喜歡不與另一個線程共享變量,因為這會增加死鎖和資源等待的可能性。 還有另一種在Go routine 之間共享資源的方法:via go channels。
通道
我們可以使用通道在兩個Go例程之間傳遞數(shù)據(jù)。在創(chuàng)建頻道時,必須指定頻道接收的數(shù)據(jù)類型。讓我們創(chuàng)建一個字符串類型的簡單通道,如下所示:
使用此通道,我們可以發(fā)送字符串類型數(shù)據(jù)。我們都可以在此頻道中發(fā)送和接收數(shù)據(jù):
接收方通道等待發(fā)送方向通道發(fā)送數(shù)據(jù)。
單向通道
在某些情況下,我們希望 Go routine 通過通道接收數(shù)據(jù)但不發(fā)送數(shù)據(jù),反之亦然。為此,我們還可以創(chuàng)建單向通道。讓我們看一個簡單的示例:
在上面的例子中,sc 是一個 Go routine ,它只能向通道發(fā)送消息但不能接收消息。
函數(shù)可能有多個通道正在等待。為此,我們可以使用select語句。讓我們看一個更清晰的例子:
在上面的例子中,main正在等待兩個通道c1和c2。使用select case語句打印主函數(shù),消息從通道發(fā)送,無論它先收到哪個。
有些情況下我們需要向通道發(fā)送多個數(shù)據(jù)。您可以為此創(chuàng)建緩沖通道。使用緩沖通道,接收器在緩沖區(qū)已滿之前不會收到消息。我們來看看這個例子:
為什么Golang成功了?
簡單…?— ?Rob-pike
我們已經(jīng)學(xué)習(xí)了 Go 語言的一些主要組件及功能。
變量,數(shù)據(jù)類型
數(shù)組分片及 map
函數(shù)
循環(huán)及條件語句
指針
包
方法,結(jié)構(gòu)和接口
錯誤處理
并發(fā) ——? Go routine 及通道
恭喜,你對 Go 已經(jīng)有不錯的理解了。
最富有成效的一天是丟棄1000行代碼。
— Ken Thompson
不要止步于此。繼續(xù)前進。構(gòu)思一個小應(yīng)用,然后開始構(gòu)建之。