var 聲明語句能夠建立一個特定類型的變量,而後給變量附加一個名字。而且設置變量的初始值。變量聲明的通常語法以下:
var 變量名字 類型 = 表達式
bool 對應的零值爲false數組
在函數內部,有一種稱爲簡單變量聲明語句用於聲明和初始化局部變量。用名字 := 表達式聲明變量,變量的類型根據表達式來自動推導。緩存
簡短變量聲明左邊的變量並非所有都是剛剛聲明的。若是有一些已經在相同的詞法域聲明過了,那麼簡短變量聲明語句對這些已經聲明過的變量就只有賦值行爲。【注】簡短變量聲明語句中必須至少要聲明一個新的變量。安全
並非每個值都會有內存地址,可是對於每個變量必然有對應的的內存地址。數據結構
任何類型的指針的零值都是nil併發
在go語言中,返回函數中局部變量的地址也是安全的,不過每次對函數調用將返回不一樣的結果函數
指針是實現標準庫中flag包的關鍵技術,他使用命令行參數來設置對應變量的值。測試
另外一個建立變量的方法是調用內建的new函數。表達式new(T)將建立一個T類型的匿名變量,初始化爲T類型的零值,而後返回變量的地址,返回的指針類型爲*T。每次調用new函數都是返回一個新的變量的地址。存在特殊狀況,就是說類型的大小是0,例如struct{}和[0]int,可能有相同的地址。this
譯註:函數的有右小括弧也能夠另起一行縮進,同時爲了防止編譯器在行尾自動插入分號而致使的編譯錯誤,能夠在末尾的參數變量後面顯式插入逗號。像下面這樣:命令行
for t := 0.0; t < cycles*2*math.Pi; t += res { x := math.Sin(t) y := math.Sin(t*freq + phase) img.SetColorIndex( size+int(x*size+0.5), size+int(y*size+0.5), blackIndex, // 最後插入的逗號不會致使編譯錯誤,這是Go編譯器的一個特性 ) // 小括弧另起一行縮進,和大括弧的風格保存一致 }
v, ok = m[key] //map查找 v, ok = x.(T) //類型斷言 v, ok = <-ch 通道接收 // _ 空白字符 medals := []string{"1", "2", "3"}
類型命名: type 類型名字 底層類型【注】類型聲明的語句通常出現包一級,若是新建類型的名字的首字母大寫,則在外部包也可使用。指針
在go語言中,若是一個名字是大寫字母開頭,那麼該名字是導出的
若是導入一個包,可是又沒有使用使用該包,將被當作一個編譯錯誤來處理。
包的初始化首先是解決包級變量的依賴順序,而後按照包級變量聲明出現的順序依次初始化,每一個包只能被初始化以一次。
字符串操做s[i:j]基於原始的s字符串的第i個字節到第j個字節生成一個新字符串。生成的新字符串將包含j-i個字節。
字符串的值是不可變的:一個字符串包含的字節序列永遠不會被改變
由於字符串是隻讀的,所以逐步構建字符串會致使不少分配和複製。在這種狀況下,使用bytes.Buffer類型將會更有效。
strconv包提供了布爾型、整形數、浮點數和對應字符串的相互轉換。
將一個整數轉換爲字符串,一種方法是用fmt.Spintf返回一個格式化的字符串;另外一個方法使用strconv.Itoa()
若是將一個字符串解析爲整數,可使用strconv包的Atoi或ParseInt函數
常量表達式的值在編譯期計算,而不是在運行期間。 多個常量const ()
若是是批量聲明的常量,除了第一個外其餘的常量右邊的初始化表達式均可以省略,若是省略初始化表達式,則表示使用前面常量的初始化表達式寫法,對應的常量了類型也是同樣
const ( a = 1 b c = 2 d )
常量聲明可使用iota常量生成器初始化,它用於生成一組類似規則初始化的常量,可是不用每行都寫一遍初始化表達式。在一個const聲明語句中,在第一個聲明的常量所在的行,iota將會被置爲0,而後每個有常量聲明的行加一
type Weekday int const ( Sunday Weekday = iota Monday Tuesday Wednesday Thursday Friday Saturday )
在數組字面值中,若是在數組的長度位置出現的是…省略號,則表示數組的長度是根據初始化值的個數來計算。
r := [...]int{99: -1} 定義一個含有100個元素的數組r,最後一個元素被初始化爲-1,其餘元素都是用0初始化
一個零值slice等於nil。一個nil值的slice並無底層數組。一個nil值的slice長度和容量都是0,可是也有非nil值的slice的長度和容量也是0的,例如[]int{}或make([]int, 3)[3:]。與任意類型的nil值同樣,咱們能夠用[]int(nil)類型轉換表達式來生成對應類型slice的nil值。【注】若是須要測試一個slice是否爲空,應該使用len(s) == 0來判斷,而不該該用s==nil來判斷。
slice建立
make([]T, len) make([]T, len, cap) // same as make([]T, cap)[:len]
map類型能夠寫爲map[K]V,建立map的方法:
Ages := make(map[string]int)
建立空的map的表達式是map[string]int{}
使用內置的delete函數能夠刪除元素:delete(ages, "Alice")
禁止對map元素取址的緣由是map可能隨着元素數據的增加而從新分配更大的內存空間,從而可能致使以前的地址無效
想要遍歷map中的所有的key/value對的話,可使用range風格的for循環實現
for name, age := range ages { }
Map的迭代順序是不肯定的,而且不一樣的哈希函數實現可能致使不一樣的遍歷順序。
若是結構體的成員是以大寫字母開頭的,那麼該成員是能夠處處的。
一個命名爲S的結構體類型將不能再包含S類型的成員:由於一個聚合的值不能包含它自身。可是能夠包含*S指針類型的成員
若是結構體沒有任何成員的話就是空結構體,寫做struct{}。它的大小是0,也不包含任何信息,可是有時候是有價值的。
當匿名函數須要被遞歸調用時,咱們必須首先聲明一個變量,再講匿名函數複製給這個變量。若是不分爲兩個部分,函數字面變量沒法與變量綁定。
defer
語句的函數會在return語句更新返回值變量後再執行,又由於在函數中定義的匿名函數能夠訪問該函數包括返回值變量在內的全部變量,因此匿名函數採用defer機制,可使其觀察函數的返回值【注】return XXX 這條語句並非一條原子指令。函數的執行過程是這樣的:先給返回值賦值,而後使用defer表達式,最後纔是返回到調用函數中。defer表達式可能會在設置函數返回值以後,在返回到使用函數以前,修改返回值。
返回值 = xxx
調用defer 函數
空的return
func double(x int) (result int) { defer func() {fmt.Printf("double(%d) = %d\n",x, result)}() return x+x }
在循環體中的defer語句須要注意,由於只有在函數執行完畢後,這些被延時的函數纔會執行。一種解決辦法將循環體中的defer語句轉移至另一個函數,在每次循環時,調用這個函數。
package geometry import "math" type Point struct{ X, Y float64 } // traditional function func Distance(p, q Point) float64 { return math.Hypot(q.X-p.X, q.Y-p.Y) } // same thing, but as a method of the Point type func (p Point) Distance(q Point) float64 { return math.Hypot(q.X-p.X, q.Y-p.Y) }
上面的p Point中的p叫作方法的接收器,在go語言中,並不會像其餘語言那樣用this或者self做爲接收器,能夠任意的選擇接收器的名字
p := Pont{1, 2} q := Point{4, 6} fmt.Println(Distance(p,q)) fms.Pringln(p.Distance(q))
p.Distance叫作選擇器,由於他會選擇合適的對應P這個對象的Distance方法來執行。
只有類型(Point)和指向他們的指針(Point),纔是可能會出如今接收器聲明裏的兩種接收器。此外,爲了不歧義,在聲明方法時,若是一個類型名自己是一個指針的話,是不容許出如今接收器中的。想用指針類型方法( Point).ScaleBy,只要提供一個Point類型的指針便可
r := &Point{1, 2} r.ScaleBy(2) fmt.Println(*r)
若是接收器p是一個Point類型的變量,而且器方法須要一個Point指針做爲接收器,可使用p.ScaleBy(2),編譯器會隱式的幫咱們用&p去調用ScaleBy這個方法,這種簡寫方法只適用於變量,包括struct裏的字段好比p.X,以及array和slice內的元素好比perim[0]
方法值和方法表達式
選擇一個方法,而且在同一個表達式裏執行,好比常見的p.Distance()形式,實際上將其分紅兩部分來執行也是可能的:p.Distance叫作選擇器,選擇器返回一個方法值->一個將方法綁定到特定接收器變量的函數。這個函數能夠不經過指定其接收器便可被調用。即調用時不須要指定接收器,只要傳入函數的參數便可。
p := Point{1, 2} q := Point{4, 6} distanceFromP := p.Distance; fmt.Println(SistnaceFromp(q))
在一個包的API須要一個函數值,且調用方法但願操做時某一個綁定了對象的方法的話,方法值會很是實用.下面例子中的time.AfterFunc這個函數的功能是在指定的延遲時間以後來執行一個(譯註:另外的)函數。且這個函數操做的是一個Rocket對象r
type Rocket struct {/**/} func (r *Rocket) Launch() {/* ....*/} r := new(Rocket) time.AfterFunc(10 * time.Second, func() { r.Launch() }) //直接用方法值傳入的話 time.AfterFunc(10*time.Second, r.Lanunch)
和方法值相關的還有方法表達式,當調用一個方法時,與調用一個普通的函數相比,咱們必需要用選擇器(p.Distance)語法來指定方法的接收器。
當T時一個類型時,方法表達式可能會寫做T.f或者(*T).f,會返回一個函數"值",這種函數會將其第一個參數用做接收器,因此能夠用一般(不寫選擇器)的方式來對其進行調用
p := Point{1, 2} q := Point{4, 6} distanceFromP := Point.Distance fmt.Println(distance(p, q)) fmt.Printf("%T\n", distance) // func(Point, Point) float64 scale := (*Point).ScaleBy scale(&p, 2) fmt.Println(p) // "{2 4}" fmt.Printf("%T\n", scale) // "func(*Point, float64)" // 這個Distance其實是指定了Point對象爲接收器的一個方法func (p Point) Distance //但經過Point.Distance獲得的函數須要比實際的Distance方法多一個參數
對於一個命名過的具體類型T;它一些方法的接收者時類型T自己然而另外一些則是一個T的指針。在T類型的參數是哪一個滴啊勇一個T的方法時合法的,但要這個參數是一個變量;編譯器隱式的獲取了它的地址。但這僅僅是一個語法糖:T類型的值不擁有全部*T指針的方法,那這樣它就肯呢個只實現更少的接口。
interface{} 被稱爲空接口類型時不可或缺的。由於空接口類型對實現它的類型沒有要求,因此咱們能夠將任意個值付給空接口類型。咱們不能直接對空接口持有的值作操做,由於interface{}沒有任何方法,能夠經過類型斷言來獲取interface{}中值得方法。
接口值:概念上一個接口的值,街口值,由兩個部分組成,一個具體的類型和那個類型的值。他們呢被稱爲接口的動態類型和動態值。類型是編譯期的概念,所以類型不是一個值。
類型斷言時一個使用在接口值上的操做。;語法上它看起來像x.(T)被稱爲斷言類型,這裏x表示一個接口的類型和T表示一個類型。一個類型斷言檢查它操做對象的動態類型是否和斷言的類型匹配。
在go語言中,每個併發的執行單元叫作一個goroutine,在語法上,go語句時一個普通的函數和方法調用前加上關鍵字go。go語句會使其語句最後那個的函數在一個新建立的gotoutine中運行。
channels時goroutine之間的通訊機制,一個channels是一個通訊機制。每一個channel都一個特殊的類型,也就是channel能夠發送的數據類型.
ch := make(chan int)
一個channel有發送和接收兩個主要操做,都是通訊行爲。一個發送語句將一個值從一個goroutine經過channel發送到另外一個執行接收操做的goroutine。發送和接收兩個操做都是用<-預算符。在發送語句中,<-運算符分割channel和要發送的值。在接收語句中,<-運算符寫在channel對象以前
ch <- x //發送 x = <- ch //接收 <-ch 、、
go語言提供了單向的channel類型,分別用於只發送或者只接收的channel。類型chan<-int表示一個只發送int的channel,只能發送不能接收。相反,類型<-chan int 表示一個只接收int的channel,只能接收不能發送。
反射是由reflect包提供支持。它定義了兩個重要的類型,Type和Value。一個Type表示一個go類型。它是一個接口。惟一能反映reflect.Type實現的是接口類型的描述信息,一樣的實體標識了動態類型的接口值
函數 reflect.TypeOf接受任意的interface{}類型,並返回對應動態類型的reflect.Type reflect.TypeOf返回的是一個動態類型的接口值,它老是會返回具體的類型。
一個reflect.Value能夠持有一個任意類型的值。函數reflect.ValueOf接受任意的interface{}類型,並返回對應動態類型的reflect.Value。和reflect.TypeOf相似,reflect.ValueOf返回的結果也是具體的類型,可是reflect.Value也能夠持有一個接口值.
一個reflect.Value和interface{}都能保存任意的值。所不一樣的是,一個空的接口隱藏了值對應的表示方式和全部的公開的方法,所以只有咱們知道具體的動態類型才能使用類型斷言來訪問內部的值,對於內部值並無特別可作的事情。
全部經過reflect.ValueOf(x)返回的reflect.Value都是不可取地址。不過能夠經過調用reflect.ValueOf(&x).Elem()來獲取任意變量x對應的可取地址的Value.能夠經過reflect.Value的CanAddr方法來判斷是否能夠被取地址。每次經過指針間接的獲取reflect.Value都是可取地址的,即便開始的是一個不可取的地址Value。在反射機制中,全部關因而否支持去地址的規則都是相似的。
要從變量對應的可取地址的reflect.Value來訪問變量須要三個步驟
x := 2 d := reflect.ValueOf(&x).Elem() // d refers to x px := d.Addr().Interface().(*int) // px = &x *px = 3
或者,不要使用指針,而是經過調用可取地址的reflect.Value的reflect.Value.Set方法來更新對於的值
d.Set(refkect.ValueOf(4))
Set方法將在運行時執行和編譯時相似的可賦值性約束的檢查
數組slice並不會實際複製一份數據,它只是建立一個新的數據結構,包含了另外的一個指針,一個長度和一個容量數據。