by 小強,2019-06-13html
go語言是目前很是火熱的語言,普遍應用於服務器端,雲計算,kubernetes容器編排等領域。它是一種開源的編譯型程序設計語言,支持併發、垃圾回收機制以提高應用程序性能。它既具備像c這種靜態編譯型語言的高性能,又具有像python這種動態語言的高效性。不少go程序員都是從C++,Java等面嚮對象語言由於工做的須要轉過來的,所以沒有必要從0開始學習go,當初本身的想法是找一篇半小時入門go的博客,貌似沒有相似的好文章=_=。搜到的都是一些從小白入手的臃腫教程,學習起來太慢!!!so,打算寫這篇go語言快速入門的指南。python
本文寫做思路重點在於和C++語言的不一樣之處入手,強調go的特性,注重快速入門,所以不介紹一些很是基礎的知識,很是基礎的一些知識能夠去看go語言聖經。關於go環境的安裝配置以及vscode編輯器的配置在以前的博客已經介紹,請移步。本文總體組織結構以下:git
在學習任何一門語言的時候,首先都是給出hello world的示例,所以本文也難免俗,看看第一個go語言程序:程序員
/*1.1 template.go*/
//當前程序的包名 package main //導入其餘的包 import ( "fmt" )//由main函數做爲函數入口 func main () { fmt.Println("Hello World!") }
和python語言很像,go程序都必須包含在一個包package中,go程序通常由三部分組成:包聲明部分、第三方包導入部分和函數聲明部分。go語言使用關鍵字package聲明要建立的包;使用import導入第三方包;使用關鍵字func聲明要建立的函數。編程
按照慣例,處於同一個文件裏的代碼文件,必須使用同一個包名,包和文件夾的名字相同。Go編譯器不容許聲明導入某個包卻不使用。使用下劃線可讓編譯器接收這類導入,而且調用對應包內的全部代碼文件中定義的init函數。 init函數會在main函數執行前執行。數組
初學者按照如下步驟編寫go程序:緩存
1)在工做目錄(好比D:\go\development)的src文件夾中建立源文件helloworld.go;.服務器
2)直接將helloworld.go拖入vscode進行編輯;閉包
3)在vscode的終端輸入go run helloworld.go,程序就會輸出hello world!併發
類型的零值:就是默認值,int默認是0,bool默認是false,字符串默認是空字符串。
類型別名方法格式:
//type 別名 基本類型 type byte int8
全局變量不能省略var關鍵字,函數內的變量聲明能夠省略。 go語言中使用關鍵字func聲明函數,關鍵字後面跟函數名、參數名以及返回值。
go語言使用關鍵字var來聲明變量,聲明的通常格式以下所示:
var <variableName> [varibleType] var count int
在聲明變量的同時可使用=給變量賦初值:
var count int = 10
其中變量類型int也能夠省略,編譯器會依據賦的初值自動推斷變量類型:
var count = 10
在聲明變量的同時還容許省略掉關鍵字「var」,使用":"取代。
count := 10
常量的聲明格式以下所示:
const <constName> [constType] = <賦值表達式>
const a = 1 const ( b, c = 2,3 ) const d,f = 4,5 const ( a = iota //0 b = iota //1 )
go的運算符均是從左至右結合。
優先級(從高到底)
其中位運算符介紹:
實際例子:
6: 0110 11:1011 ------------- &: 0010 |: 1111 ^: 1101 &^:0100
Go雖然保留了指針,但不一樣點在於go不支持指針運算以及->運算符,而直接採用.選擇符來操做指針目標對象的成員。
A++//只能做爲單獨的語句放在一行,且只能++放在右邊 x, y := 1, 2 var p = [2]*int{&x, &y} fmt.Println(*p[0]) var arr = [2]int{x, y} pf := &arr fmt.Println(*pf)
其實就是將[]*int當作一個類型,後面的{}就是初始化操做。
if a > 1 { fmt.Println(a) } if b :=1;b > 1 { }
注意else必須和if的右括號}在同一行才行,否則出錯。
if a { }else {}
if後面定義的變量,屬於if語句的局部變量,只能在對應的if-else中使用,不能在外部使用。之間經過;分割語句。
1. for init; condition; post {} // 和c語言的for同樣 2. for condition {} //while 3. for {} //for(;;) init: 通常爲賦值表達式,給控制變量賦初值;必定在這裏賦初值,否則出錯 condition: 關係表達式或邏輯表達式,循環控制條件; post: 通常爲賦值表達式,給控制變量增量或減量。
for語句執行過程以下:
1)先對錶達式1賦初值;
2)判別賦值表達式init是否知足給定條件,若其值爲真,知足循環條件,則執行循環體內語句,而後執行post,進入第二次循環,再判別condition;不然判斷condition的值爲假,不知足條件,就終止for循環,執行循環體外語句。
for key, value := range oldMap { newMap[key] = value }
關鍵字range會返回兩個值,第一個值是當前迭代到的索引位置,第二個值是該位置對應元素值的一份副本。而不是返回對該元素的引用。
switch var1 { case val1: ... case val2: ... default: ... }
跳轉語句goto,break,continue
定義數組的格式: var <varName> [n]<type> ,n >= 0(表示數組的元素個數)。
var a [2]int var b [1]int
記住ab是不一樣的類型,不能直接賦值,元素個數也是數組類型的一種。須要使用循環語句進行操做。 也能夠不指定數組元素的個數。
a := [...]int{1,2,3} //元素個數爲3個 a := [...]int{0:1,1:2}//位置0賦值爲1,位置1賦值爲2 a := new([10]int)
須要注意的是全部值類型變量在賦值或做爲參數傳遞的時候將產生一次複製操做。若是將數組做爲函數的參數類型,則在函數調用是將該參數將發生數據複製,函數體內沒法修改數組的內容,由於函數體內操做是變量的一個副本。
多維數組的聲明以下所示,其中第一維度行的數量是能夠省略的,使用...代替。
arr := [2][3]int{ {1, 2, 3}, {2, 3, 4}} 表示2個元素,每一個元素是一維數組,有三個元素。
切片是數組的一個引用,它會生成一個指向數組的指針,並經過切片長度關聯到底層數組部分或者所有元素,還提供了一系列對數組的管理功能(append,copy),能夠隨時動態的擴充存儲空間。屬於變長數組,至關於C++的vector。建立切片的格式以下:
var sliceName []dataType
建立切片時,不須要指定切片的長度。下面是一個具體的例子。
var slice1 []int
6.2.1 初始化方法
1)若是引用底層數組的元素,初始化方法以下:
slice1 = array[start : end] //如下是三種方式 slice1 = array1 slice1 = array1[ : ] slice1 = array[0 : len(array1)]
2)直接建立切片
即在定義的同時初始化切片元素,以下例:
var slice1 = []int{1,2,3,4,5}
3)使用make函數建立切片
下式表示,建立整型切片slice1,元素個數爲5,元素初值爲0,並預留10個元素的存儲空間。
var slice1 = make([]int, 5, 10)
對切片的訪問方式能夠經過下標的方式訪問,也能夠經過range關鍵字進行訪問,同數組。
//從數組初始化 var arr = [...]int{1,2,3} var slice_a []int slice_a = arr[1:2]//下標位置,[1,2),包括首位置,不包含末尾的2位置
6.2.2 切片的操做
- Reslice:
- Append
append(s1,s2...)
- copy
copy(s1,s2),必須保證s1有足夠的空間來存儲s2的值。
- 多維切片
slice := [][]int{{1, 2}, {3, 4}}
使用切片作值函數傳遞時,以值的形式傳遞切片,因爲切片的尺寸很小,因此成本很低,與切片關聯的數據保存在底層數組中,不屬於切片自己,因此切片的效率很高。slice的拷貝可使用 s2 := s1[:],拷貝首元素省略,拷貝末尾元素也能夠省略,:表示拷貝所有元素。
map就是理解爲C++裏面的map,是key-value類型,也稱爲字典或者哈希表。
6.3.1 聲明格式
var mapName map[keyType] valueType var map1 map[string] int
在該例中,聲明瞭一個鍵值類型爲字符串,值類型爲整型的字典map1。
6.3.2 字典的初始化和建立
使用「{ }」操做符對字典進行初始化操做,或者使用make()函數來建立字典。初始化或者建立後,就可使用「=」操做符對字典動態的增添數據項了。
var map1 map[string] int {} map1["key1"] = 1
也可使用下面的方式進行建立:
var map1 map[string] int map1 = make(map[string] int) map1["key1"] = 1
6.3.3 map的訪問和操做
map經過key來訪問value,訪問格式以下所示:
Value = mapName[Key]
map的查找:若是查找的key存在,則將key對應的value值賦予v,OK爲true,反之,若是Key不存在,則v等於0,OK爲false。
v,OK := mapName[Key]
map的刪除:
delete()用於刪除容器內的元素,也能夠用於刪除map內的鍵值對,例如:
下面將從map1中刪除鍵值爲key1的鍵值對,若是key1這個鍵不存在,那麼這個調用將什麼也不會發生。
delete(map1,「key1」)
相似其餘語言中的哈希表或者字典,以key-value形式存儲數據
key必須是支持==或!=比較運算的類型,不能夠是函數、map或者slice
map查找比線性搜索快不少,但比使用索引訪問數據的類型慢100倍
map使用make()建立,支持:=這種簡寫方式。
make([keyType]valueType,cap),cap表示容量,可省略
超出容量時會自動擴容,但儘可能提供一個合理的初始值
使用len()獲取元素個數
鍵值對不存在時自動添加,使用delete()刪除某鍵值對
使用for range對map和slice進行迭代操做
記住每一個map都必須進行單獨的初始化操做。 使用make進行初始化操做。有幾層map就須要使用幾回make進行初始化操做。
for k,v := range m {}
func functionName(參數列表) 返回值 { functionBody . . . return 語句 }
func A (a ...int) {}
func f(i int) func() int { return func() int { i++ return i } }
go中能夠拋出一個panic的異常,而後在defer中經過recover捕獲這個異常,而後正常處理
func B() { defer func() { if err := recover(); err != nil { fmt.Println("Recover in B") } }() panic("Panic in B") }
func main() { var fs = [4]func(){} for i := 0; i < 4; i++ { defer fmt.Println("defer i = ", i) defer func() { fmt.Println("defer_closure i = ", i) }() fs[i] = func() { fmt.Println("closure i = ", i) } } for _, f := range fs { f() } }
由於defer是逆序執行的,在i變爲4以後,閉包中指向的是i的地址,因此閉包中的i的值都是指向i=4的地址。
go語言的method相似於一個函數,只是函數名前多了個綁定類型參數---receiver,基本格式以下:
func (recv receiver_type) methodName (參數列表)(返回值){...}
method中的receiver能夠是內置類型、自定義類型、結構體或指針類型。
不一樣包中大小寫變量方法纔有權限的區別,同一個包中能夠訪問private字段的內容,大寫的public權限能夠被不一樣包之間訪問。type tz int,記住tz i和int i仍是不一樣的類型,前面的i屬於tz類型。要二者相加必須使用強制類型轉換。
接口是用來定義行爲的類型,這些被定義的行爲不禁接口直接實現,而是經過方法由用戶定義的類型實現。
類型斷言
接口轉換
MethodByName()方法使用原對象的方法名name獲取該方法的Value值,若是所訪問的方法不存在,MethodByName會返回0.
在go語言中傳遞給方法的參數要和方法定義的參數類型保持一致,爲了處理變參這種複雜狀況,傳遞給被調用方法的參數一般首先保存在一個Slice中,而後在複製到參數列表中。
var varName chan elementType var c chan int //和普通變量相比,只是增長了一個chan ch := make(chan int) // 使用make函數直接聲明並初始化channel
Channel的主要用途是在不一樣的Goroutine之間傳遞數據,它使用通道運算符<-接收和發送數據,將一個數據發送(寫入)至channe的方法是 ch <- value
value := <- ch
close(chanName)
,在關閉一個channel以後,用戶還須要判斷Channel是否關閉,可使用多重返回值的方法:value,ok := <- ch
只須要看第二個bool返回值極客,若是返回值是false則表示Channel已關閉。
var chanName chan <- ElementType
var chanName <- chan ElementType
在定義了Channel以後,還須要對其進行初始化操做,能夠由一個已定義的雙向Channel轉換而來。ch := make(chan int) chRead := <- chan int(ch) chWrite := chan <- int(ch)
- 異步Channel
在Goroutine間傳輸大量數據的時候,可使用異步通道(Asynchronous-channel),類比消息隊列的效果。
異步Channel就是給Channel設定一個buffer值,在buffer未寫滿的狀況下,不阻塞發送操做。buffer指的是被緩衝的數據對象的數量,而不是指內存大小。
ch := nake(chan int,1024)
該例建立了一個1024的int類型的Channel。select機制每一個case語句必須是一個I/O操做,其基本結構以下:
select { case <- chan1: //若是chan1成功讀取數據,則進行該case處理語句。 case <- chan2: //若是chan2成功讀取數據,則進行改該case處理語句。 default: // 若是上面都沒有成功,則進入default處理流程。 }