Go語言新手入門淺談
1、 前言
因爲工做中存在與Go語言相關的內容,所以最近花費部分時間對Go語言進行了解,從基礎語法開始對Go語言開始學習。Go語言語法簡單,類C語法的特性致使學習Go語言學習容易,可以極快上手,然而如果但願深刻理解Go語言仍需在項目實踐中不斷錘鍊。前端
本篇文章首先淺談我對Go語言誕生環境、語言特點等內容的瞭解,而且總結Go語言的基礎語法,以做交流。本篇文章從一個新手學習Go語言的角度編寫,若文中存在需修正之處,歡迎評論留言指正。java
2、 Go語言行業應用
Go語言是2007年底由Robert Griesemer, Rob Pike, Ken Thompson主持開發,後來還加入了Ian Lance Taylor, Russ Cox等人。Go語言於2009年11月開源,在2012年3月發佈了Go 1.0穩定版本。如今Go的開發已是徹底開放的,而且擁有一個活躍的社區[1]。git
Go語言的誕生存在其特殊的氛圍背景。2007年,Google的首席軟件工程師Rob Pike在等待C++編譯的漫長等待過程當中,拉上另外兩位大佬,Robert Griesemer和Ken Thompson,指望可以創造出另一門可以擁有接近C的執行性能、接近解析性語言的開發效率以及高效的編譯速度的開發語言取代C++。三位大佬將此項目命名爲Golang,並將它做爲自由時間的實驗項目。在大約一年後,Google發現了Go語言的巨大潛力而且全力支持,最終發展到目前的地步。此誕生背景最終決定了Go語言的兩大特點:類C特性以及爲服務端服務。
github
項目名稱 | 項目簡介 |
---|---|
Docker | golang頭號優秀項目,經過虛擬化技術實現的操做系統與應用的隔離,也稱爲容器; |
Kubernetes | 由google開發,簡稱k8s,k8s和docker是當前容器化技術的重要基礎設施; |
Etcd | 一種可靠的分佈式KV存儲系統,有點相似於zookeeper,可用於快速的雲配置; |
TiDB | 國內PingCAP團隊開發的一個分佈式SQL數據庫,國內不少互聯網公司在使用; |
InfluxDB | 時序型DB,着力於高性能查詢與存儲時序型數據,經常使用於系統監控與金融領域; |
CockroachDB | 雲原生分佈式數據庫,繼NoSQL以後出現的新的概念,稱爲NewSQL數據庫; |
Beego | 國人開發的一款及其輕量級、高可伸縮性和高性能的web應用框架; |
go-kit | Golang相關的微服務框架,這類框架還有go-micro、typhon; |
go-ethereum | 官方開發的以太坊協議實現; |
除了上表中展現的項目以外,國內外各大互聯網巨頭也逐漸將Go語言做爲雲上服務的技術棧使用。例如:以今日頭條爲例,早在2017年,今日頭條使用Go語言構建了承載了超過80%流量的後端服務,高峯QPS超過700萬,日處理請求量超過3000億。此外京東的消息推送與分佈式存儲、知乎的推薦系統、網易雲信的調度服務等均開始使用Go語言。
golang
3、 IDE介紹
1. Goland
JetBrains做爲一家推出過許多功能強大IDE的公司,迄今爲止已推出包括:Java的Intellj IDEA、PHP的PHPStorm、 Python的Pycharm、前端的WebStorm在內的集成開發環境。它旗下推出了一款專門面向Go語言的集成開發環境Goland,功能強大,使用十分便捷。Goland社區的插件豐富,功能齊全,使用過程當中碰到的問題少,開箱即食,對新手十分友好。可在jetbrains官網下載Goland產品,下載頁面:https://www.jetbrains.com/go/。與IDEA等存在面向我的用戶的免費版本不一樣的是,Goland僅有收費版本,使用者在免費試用30天后需購買以獲取使用權限。web
2. VSCode
VSCode是目前比較流行的輕量型IDE,集成了衆多語言的插件,以支持多種語言的開發,其中包括Go語言插件。本段將介紹如何使用VSCode搭建Go語言的開發環境。docker
- 下載VSCode,下載連接爲:https://code.visualstudio.com
- 進入VSCode,安裝Golang插件,以下圖所示,依據次序點擊執行: 選擇插件市場->搜索Go插件->認準微軟出品->點擊安裝。
- 在VSCode的Setting中配置User Settings中的GOROOT及GOPATH,其餘配置默認便可。如圖4所示:
其中:GOROOT是Go的安裝路徑,GOPATH是編譯後二進制的存放目的地和import包時的搜索路徑,也是你的工做目錄。
- 此時新建HelloWorld.go,編寫代碼,點擊運行後,控制檯會返回提示,有工具須要安裝,可點擊右下角彈出的提示框install All進行一鍵安裝,如圖5所示。也可打開命令提示符輸入以下命令進行工具安裝(本地需安裝git):
安裝gocode:go get -u -v github.com/nsf/gocode
安裝gopkgs:go get -u -v github.com/tpng/gopkgs
安裝go-outline:go get -u -v github.com/lukehoban/go-outline
安裝godef:go get -u -v github.com/rogpeppe/godef
安裝golint:
cd $GOPATH/src/golang.org/x
git clone https://github.com/golang/tools.git
git clone https://github.com/golang/lint.git
go get golang.org/x/lint/golint
數據庫
在安裝過程當中,因爲GFW的緣由,可能會致使部分工具源不可用,此問題可經過從Github使用git clone下載到本地再使用本地安裝解決後端
圖5 VSCode Go環境配置-工具安裝- 安裝完畢後重啓VSCode,便可完成Go語言插件的安裝。
4、 語法介紹
1. Hello World
建立hello.go,而且鍵入以下代碼,運行Hello World:數組
package main import "fmt" func main() { fmt.Println("Hello, World!"); }
在此代碼中,包含三塊主要信息:package、import、func。其中,一個獨立go程序須要至少一個main package。此外,還需經過func main聲明主函數入口,經過import引入 標準化輸入輸出包,以實現Hello Wolrd 字符串的打印。
關於package的使用,細節以下所示:
a. Go程序是由一個或多個package組成,若是獨立程序,則須要一個main package
b. 每個package由若干個源文件組成,每一個go源文件均需頭部聲明自身所屬的package。
c. 其中同屬於一個package的源文件應當存放於同一個目錄之下
d. 在使用其餘package的包內容時,能夠經過import packagename 「[package path]」來實現,代碼以下:
package study import packageName "path/to/package"
2. 變量聲明
Go語言的變量聲明方式很是簡單且易於閱讀,最常規的方式爲經過var關鍵字聲明變量: var variable_name variable_type,關鍵字、變量名、變量類型之間以空格分隔,實例代碼以下所示:
func main() { var a int a = 20 fmt.Println(a) }
值得注意的是,Go語言的規範十分嚴格,設計人員將編碼規範融入Go語言的編譯規則中。以此實例爲例,若僅僅定義、賦值了變量a而未使用,編譯時會報錯。
除此以外,變量的聲明及初始化賦值還存在其餘方式,以下所示:
func main() { var a, b int = 20, 30 var c = "HelloWorld" str := "" fmt.Println(a, b, c, str) }
代碼中各變量聲明方式含義以下所示:
- 定義int型的兩個變量a和b,而且分別賦值爲20和30
- 定義新變量c,而且賦予初始值,由編譯器編譯時自動決定變量c的類型
- str := 「」 的做用 等同於 var str = 「」,表示定義新變量、初始化賦值,而且由編譯器編譯時自動決定變量str的類型。值得注意的是:=左邊必須是未定義過的變量名,已定義過變量使用:=賦值會在編譯時出錯
3. 函數與方法
Go語言中的函數
函數是基本的代碼塊,用於執行不一樣的業務任務流程。函數定義時指定函數名稱、參數以及返回類型,具體格式以下所示:
func function_name( [parameter list] ) [return_types] { 函數體 }
函數由func關鍵字開始聲明,並依次包含函數名、參數列表、返回值列表。值得注意的是,Go語言中的函數可有無返回值、僅返回一個值和返回多個值三種狀況可選。其中,返回多個值的實例代碼以下所示:
func swap(x, y int) (int, int) { return y, x } func main() { a, b := 1, 2 a, _ = swap(a, b) fmt.Println(a, b) }
當調用多返回值的函數時,須要有對應數量的變量對象接收函數返回值。若不須要多個返回值中的某幾個,可使用Go語言中的空白標識符 _ 佔位。
Go語言中的方法
方法也是函數,它帶有一個特殊的接收器類型,它是在func關鍵字和方法名之間編寫的。能夠在方法內部訪問接收器。方法能給用戶自定義的類型添加新的行爲。它和函數的區別在於方法有一個接收者,給一個函數添加一個接收者,那麼它就變成了方法。實例代碼以下所示:
type Person struct { age int name string gender string } func (p Person) Growup1() Person { p.age++ return p } func Growup2(p Person) Person { p.age++ return p } func main() { p := Person{name: "張三",age: 25, gender: "男"} p = p.Growup1() fmt.Println(p) }
在此代碼中,Groupup1與Groupup2二者實現的功能及效果一致。
4. 結構體
結構體的類型在java、c等語言之中均存在,同時也是十分重要的類型。經過結構體的使用將不一樣類型的數據組合造成新的數據類型。Go語言中一樣存在結構體的類型。結構體的定義以下代碼所示:
type struct_variable_type struct { member definition; member definition; ... member definition; }
當定義告終構體後,便可將結構體應用於變量的聲明。普通變量的聲明及賦值方法一樣適用於結構體變量的聲明及初始化,以下列實例所示:
type Person struct { name string age int gender string } func main() { var p Person fmt.Println(p) }
此處應值得注意的是,Go語言經過首字母大小寫來控制訪問權限,不管是變量、常量、方法仍是自定義類型,首字母大寫便可被外部包訪問,反之則不行。以此實例爲例,此處Person類型可被外部包訪問,若類型名爲person則不行。
除此以外結構體擁有多種初始化方法,包括:
func main() { p1 := Person{"張三", 25, "男"} p2 := Person{ gender: "女",name: "李四", age: 25} p3 := new(Person) }
代碼中各方式含義以下:
- 按照結構體中成員變量定義的順序依次提供初始化值。
- 經過field:value的方式初始化,此方式無需按照結構體中成員變量定義的順序提供值,可任意改變順序。
- 經過new方式初始化結構體,結構體中各成員變量會賦予類型默認的初始值。
結構體的訪問
在上述初始化方式以後,p1與p2可經過.點操做符用於訪問結構的各個字段,以獲取或更改字段的值。P3經過new方式初始化,所得的結果爲結構體指針。
// The new built-in function allocates memory. The first argument is a type, // not a value, and the value returned is a pointer to a newly // allocated zero value of that type. func new(Type) *Type
在C語言中此狀況須要使用來訪問對象並操做成員變量,而在Go語言中無需使用,可直接經過點.操做符訪問指針對象以及結構中的各個字段。具體代碼以下所示:
func main() { p1 := Person{"張三", 25, "男"} p2 := Person{ gender: "女",name: "李四", age: 25} p3 := new(Person) p3.age = 18 p3.name = "Cristina" }
結構體的比較
結構體是值類型,若是結構體中的成員變量是能夠比較的,那結構體是能夠比較的。若是結構體中對應的字段均相等,則認爲兩個結構體變量相等。實例代碼以下所示:
func main() { p1 := Person{"張三", 25, "男"} p2 := Person{"張三", 25, "男"} if p1 == p2 { fmt.Println("Person1 and Person2 are equal") } }
運行結果以下:
Person1 and Person2 are equal
5. 循環控制
Go語言中循環過程控制主要經過for實現循環執行操做。和java語言相似,For語句的基本語法結構爲:
For init; condition; post {}
其中:初始化語句僅被執行一次,在初始化語句執行以後,循環體執行以前將檢查condition的條件,若是condition的值爲true則執行循環體,不然結束循環。在一次循環體結束後則執行post語句,在此以後再次檢查condition狀態,並依據結果決定是否繼續循環。
在Go語言中沒有while關鍵字,可是能夠經過for語句實現while的效果,while效果的for語句的基本語法結構爲:
For condition {}
此外,可使用for語句配合range關鍵字對slice、map、數組進行遍歷,此實例代碼以下所示:
func main() { array := [...]int{1,2,3,4,5,6,7,8} for i,v := range array { fmt.Printf("數組中第 %d 個值爲 %d \n", (i + 1), v) } }
6. 內建容器
Go語言中,除了數字類型、字符類型等基本類型外,還包括數組、切片、Channel等派生類型。本篇文章中主要記錄數組、切片兩種數據類型的使用。
數組
與C語言中數組的概念一致,Go語言中數組也是一段固定長度的連續內存區間。數組的聲明須要指定元素類型以及元素個數,語法格式以下:var variable_name [SIZE] variable_type
以此格式爲標準,可得以下實例:
var arr1 [5]int
此實例中,定義了長度爲5的整數型數組,而且爲全部元素賦予初始值爲0。除此以外,Go語言提供了其餘聲明並初始化數組的方式,具體如實例所示:
func main() { var arr1 [5]int arr2 := [4]int{1,2,3,4} arr3 := [...]int{1,2,3,4,5,6,7,8} fmt.Println(arr1, arr2, arr3) }
在第二種方式中,指定數組長度並使用 := 而非var關鍵字聲明,此方法要求聲明的同時必須賦予初始化的值,而且初始化數組內元素的數量必須等於指定的數組長度。
在第三種方式中,可無需指定數組長度,Go語言會根據元素的個數自動設置數組的大小。但須要注意:「[ ]」中必須使用「…」,若無「…」 獲得的結果則不是數組而是切片。
切片
Go語言的數組定義完成後長度不可變,在某些特定場景下數組不太適用。Go語言原生支持一種靈活的內置類型:切片。切片是動態分配大小的連續空間,切片自己能夠不儲存數據,僅產生對底層數組的一個視圖,從而靈活地對切片進行數據處理(相似「動態數組」),並經過此方法對底層數組的數據進行處理。此處視圖的概念與數據庫中的視圖概念相似。
切片的內部結構包含地址、大小與容量。其中:
地址:即此段連續內容空間的起始地址
大小:即切片可取值的範圍長度。長度可擴展。
容量:即切片所對應底層數組中,從起始地址開始的可用長度。容量可向後擴展,不可向前擴展。
從數組或切片中生成新的切片
切片默認指向一段連續內存區域,能夠是數組和切片自己,在一段連續內存區域生成切片是一種生成新切片的典型方式。格式代碼以下:
slice[startindex : endindex]
其中:
- slice表示切片目標對象
- startindex表示切片對象的起始索引
- endindex表示切片對象的結束索引
值得注意的是,此方法生成的切片僅僅是源連續內存區域的視圖,自己不存儲數據,所以對此方法生成切片的數據的更改會直接反應到內存區域,即:被切片的源切片和數組對應值也會更改。以以下實例代碼爲例:
func main() { a := []int{1,2,3,4} b := a[0:] b[0] = 10 fmt.Println(a, b) }
運行結果爲:
[10 2 3 4] [10 2 3 4]
聲明切片以生成新切片
每種類型,包括自定義類型,均可以擁有其切片類型,以表示多個該類型元素的連續集合,此方法聲明格式以下:
var slice_name []type_name
其中:
- Slice_name表示切片類型的變量名
- Type_name 表示該切片中對應的元素類型
使用make()函數構造新切片
可使用make()函數動態地建立一個新的切片,該方法格式以下:
make( []type_name, len, cap )
其中:
- Type_name:建立的新切片的元素類型
- Len:爲該切片分配多少初始元素
- Cap:提早分配的內存區域大小。Cap的設計主要爲了提早分配空間,以防止擴展時可能的再次分配空間而影響性能。
當slice擴展至超過容量邊界時,系統會自動擴展容量,容量擴展策略是擴展至當前容量的雙倍,如下列實例代碼爲例:
func testslice() { a := make([]int, 3) fmt.Println(len(a), cap(a)) a = append(a, 3) fmt.Println(len(a), cap(a)) } func main() { testslice() }
運行結果爲:
3 3
4 6
映射(map)
map是十分重要的數據結構,用以存儲任意類型數據的關聯關係。Go語言中,map類型定義格式以下所示:
map[KeyType]ValueType
實例代碼以下所示:
func testmap() { m := make(map[string]int) m["id1"] = 10001 fmt.Println(m["id1"]) } func main() { testmap() }
此外,map支持在聲明時初始化填充內容,實例代碼以下:
m := map[string]int{ "id1":10001, "id2":10002, "id3":10003, }
map的遍歷
map的遍歷可以使用range關鍵字完成循環,range關鍵字可依次返回map中的key與value,同時得到鍵與值。實例代碼以下:
func getall() { m := map[string]int{ "id1":10001, "id2":10002, "id3":10003, } for k,v := range m { fmt.Println("key : " , k, " value : " , v) } } func main() { getall() }
運行結果爲:
key : id1 value : 10001
key : id2 value : 10002
key : id3 value : 10003
5、 總結
Go語言語法簡單,經過以上基礎語法學習後,可使用Go語言實現簡易功能,然而Go語言中依然存在許多特性,須要長期練習實踐、閱讀源碼纔可深刻理解。此外Go語言中還可利用goroutine、 channel實現高性能併發,在閒暇及項目中多多思考以熟練使用。
參考資料:
[1]. 爲何要使用 Go 語言?Go 語言的優點在哪裏? - 騰訊雲技術社區的回答。點擊跳轉
[2]. 爲何要使用 Go 語言?Go 語言的優點在哪裏? - 波羅學的回答。點擊跳轉