最近在讀《Go 語言程序設計》這本書想經過看書鞏固一下本身的基礎知識,把已經積累的點經過看書學習再編織成一個網,這樣看別人寫的優秀代碼時才能更好理解。當初工做中須要使用 Go開發項目時看了網上很多教程,好比 uknown 翻譯的《the way to go》看完基本上每次使用遇到不會的時候還會再去翻閱,此次把書中的重點還有一些平時容易忽視的Go語言中各類內部結構(類型、函數、方法)的一些行爲整理成讀書筆記。git
由於《Go 語言程序設計》不是針對初學者的,因此我只摘選最重要的部分並適當補充和調換描述順序力求用最少的篇幅描述清楚每一個知識點。github
《Go 語言程序設計》在線閱讀地址:yar999.gitbooks.io/gopl-zh/con…數組
若是剛接觸 Go建議先去讀 《the-way-to-go》在線閱讀地址:github.com/unknwon/the…緩存
函數名、變量名、常量名、類型名、包名等全部的命名,都遵循一個簡單的命名規則:一個名字必須以一個字母(Unicode字母)或下劃線開頭,後面能夠跟任意數量的字母、數字或下劃線。安全
大寫字母和小寫字母是不一樣的:heapSort和Heapsort是兩個不一樣的名字。bash
關鍵字不可用於命名數據結構
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
複製代碼
推薦駝峯式命名函數
名字的開頭字母的大小寫決定了名字在包外的可見性。若是一個名字是大寫字母開頭的,那麼它能夠被外部的包訪問,包自己的名字通常老是用小寫字母。學習
var聲明語句能夠建立一個特定類型的變量,而後給變量附加一個名字,而且設置變量的初始值。變量聲明的通常語法以下:ui
var 變量名字 類型 = 表達式
複製代碼
其中「類型」或「= 表達式」兩個部分能夠省略其中的一個。若是省略的是類型信息,那麼將 根據初始化表達式來推導變量的類型信息。若是初始化表達式被省略,那麼將用零值初始化該變量。 數值類型變量對應的零值是0,布爾類型變量對應的零值是false,字符串類型對應的零值是空字符串,接口或引用類型(包括slice、map、chan和函數)變量對應的零值是nil。數組或結構體等聚合類型對應的零值是每一個元素或字段都是對應該類型的零值。
零值初始化機制能夠確保每一個聲明的變量老是有一個良好定義的值,所以在Go語言中不存在未初始化的變量。這個特性能夠簡化不少代碼,並且能夠在沒有增長額外工做的前提下確保邊界條件下的合理行爲。例如:
var s string
fmt.Println(s) // ""
複製代碼
文本字符串一般被解釋爲採用UTF8編碼的Unicode碼點(rune)序列。
內置的len函數能夠返回一個字符串中的字節數目(不是rune字符數目),索引操做s[i]返回第i個字節的字節值,i必須知足0 ≤ i< len(s)條件約束。
字符串的值是不可變的:一個字符串包含的字節序列永遠不會被改變,固然咱們也能夠給一個字符串變量分配一個新字符串值。能夠像下面這樣將一個字符串追加到另外一個字符串:
s := "left foot"
t := s
s += ", right foot"
複製代碼
這並不會致使原始的字符串值被改變,可是變量s將由於+=語句持有一個新的字符串值,可是t依然是包含原先的字符串值。
由於字符串是不可修改的,所以嘗試修改字符串內部數據的操做也是被禁止的:
s[0] = 'L' // compile error: cannot assign to s[0]
複製代碼
每個UTF8字符解碼,不論是顯式地調用utf8.DecodeRuneInString解碼或是在range循環中隱式地解碼,若是遇到一個錯誤的UTF8編碼輸入,將生成一個特別的Unicode字符'\uFFFD',在印刷中這個符號一般是一個黑色六角或鑽石形狀,裏面包含一個白色的問號"�"。當程序遇到這樣的一個字符,一般是一個危險信號,說明輸入並非一個完美沒有錯誤的UTF8字符串。
字符串的各類轉換:
string接受到[]rune的類型轉換,能夠將一個UTF8編碼的字符串解碼爲Unicode字符序列:
// "program" in Japanese katakana
s := "プログラム"
fmt.Printf("% x\n", s) // "e3 83 97 e3 83 ad e3 82 b0 e3 83 a9 e3 83 a0"
r := []rune(s)
fmt.Printf("%x\n", r) // "[30d7 30ed 30b0 30e9 30e0]"
複製代碼
(在第一個Printf中的% x
參數用於在每一個十六進制數字前插入一個空格。)
若是是將一個[]rune類型的Unicode字符slice或數組轉爲string,則對它們進行UTF8編碼:
fmt.Println(string(r)) // "プログラム"
複製代碼
將一個整數轉型爲字符串意思是生成以只包含對應Unicode碼點字符的UTF8字符串:
fmt.Println(string(65)) // "A", not "65"
fmt.Println(string(0x4eac)) // "京"
複製代碼
若是對應碼點的字符是無效的,則用'\uFFFD'無效字符做爲替換:
fmt.Println(string(1234567)) // "�"
複製代碼
數組的長度是數組類型的一個組成部分,所以[3]int和[4]int是兩種不一樣的數組類型。數組的長度必須是常量表達式,由於數組的長度須要在編譯階段肯定。
q := [3]int{1, 2, 3}
q = [4]int{1, 2, 3, 4} // compile error: cannot assign [4]int to [3]int
複製代碼
長度對應slice中元素的數目;長度不能超過容量,容量通常是從slice的開始位置到底層數據的結尾位置。內置的len和cap函數分別返回slice的長度和容量。
x[m:n]切片操做對於字符串則生成一個新字符串,若是x是[]byte的話則生成一個新的[]byte。
slice並非一個純粹的引用類型,它其實是一個相似下面結構體的聚合類型:
type IntSlice struct {
ptr *int
len, cap int
}
複製代碼
在Go語言中,一個map就是一個哈希表的引用,map類型能夠寫爲map[K]V,其中K和V分別對應key和value。map中全部的key都有相同的類型,全部的value也有着相同的類型,可是key和value之間能夠是不一樣的數據類型。
map中的元素並非一個變量,所以咱們不能對map的元素進行取址操做:
_ = &ages["bob"] // compile error: cannot take address of map element
複製代碼
禁止對map元素取址的緣由是map可能隨着元素數量的增加而從新分配更大的內存空間,從而可能致使以前的地址無效。
map上的大部分操做,包括查找、刪除、len和range循環均可以安全工做在nil值的map上,它們的行爲和一個空的map相似。可是向一個nil值的map存入元素將致使一個panic異常:
ages["carol"] = 21 // panic: assignment to entry in nil map
複製代碼
在向map存數據前必須先建立map。
和slice同樣,map之間也不能進行相等比較;惟一的例外是和nil進行比較。要判斷兩個map是否包含相同的key和value,咱們必須經過一個循環實現。
下面兩個語句聲明瞭一個叫Employee的命名的結構體類型,而且聲明瞭一個Employee類型的變量dilbert:
type Employee struct {
ID int
Name string
Address string
DoB time.Time
Position string
Salary int
ManagerID int
}
var dilbert Employee
複製代碼
dilbert結構體變量的成員能夠經過點操做符訪問,好比dilbert.Name和dilbert.DoB。由於dilbert是一個變量,它全部的成員也一樣是變量,咱們能夠直接對每一個成員賦值:
dilbert.Salary -= 5000 // demoted, for writing too few lines of code
複製代碼
或者是對成員取地址,而後經過指針訪問:
position := &dilbert.Position
*position = "Senior " + *position // promoted, for outsourcing to Elbonia
複製代碼
若是結構體成員名字是以大寫字母開頭的,那麼該成員就是導出的;這是Go語言導出規則決定的。一個結構體可能同時包含導出和未導出的成員。未導出的成員只能在包內部訪問,在外部包不可訪問。
結構體類型的零值中每一個成員其類型的是零值。一般會將零值做爲最合理的默認值。例如,對於bytes.Buffer類型,結構體初始值就是一個隨時可用的空緩存,還有sync.Mutex的零值也是有效的未鎖定狀態。有時候這種零值可用的特性是天然得到的,可是也有些類型須要一些額外的工做。
由於結構體一般經過指針處理,能夠用下面的寫法來建立並初始化一個結構體變量,並返回結構體的地址:
pp := &Point{1, 2}
複製代碼
Go語言有一個特性讓咱們只聲明一個成員對應的數據類型而不指名成員的名字;這類成員就叫匿名成員。匿名成員的數據類型必須是命名的類型或指向一個命名的類型的指針。下面的代碼中,Circle和Wheel各自都有一個匿名成員。咱們能夠說Point類型被嵌入到了Circle結構體,同時Circle類型被嵌入到了Wheel結構體。
type Circle struct {
Point
Radius int
}
type Wheel struct {
Circle
Spokes int
}
複製代碼
得益於匿名嵌入的特性,咱們能夠直接訪問葉子屬性而不須要給出完整的路徑:
var w Wheel
w.X = 8 // equivalent to w.Circle.Point.X = 8
w.Y = 8 // equivalent to w.Circle.Point.Y = 8
w.Radius = 5 // equivalent to w.Circle.Radius = 5
w.Spokes = 20
複製代碼
外層的結構體不只僅是得到了匿名成員類型的全部成員,並且也得到了該類型導出的所有的方法。這個機制能夠用於將一個有簡單行爲的對象組合成有複雜行爲的對象。