變量
var num int;var num int = 1,var a,b int;var a,b int = 1,2;var a = 1;_,b=1,2;
1纠拔,:=這個符號直接取代了var和type,這種形式叫做簡短聲明。不過它有一個限制泛豪,那就是它只能用在函數(shù)內(nèi)部稠诲;在函數(shù)外部使用則會無法編譯通過,所以一般用var方式來定義全局變量诡曙。
2臀叙,_(下劃線)是個特殊的變量名,任何賦予它的值都會被丟棄价卤。
3劝萤,Go對于已聲明但未使用的變量會在編譯階段報錯
常量
const Pi=3.1415926;const i =10000;const MaxThread=10;const prefix ="astaxie_"
所謂常量,也就是在程序編譯階段就確定下來的值荠雕,而程序在運(yùn)行時無法改變該值稳其。在Go程序中,常量可定義為數(shù)值炸卑、布爾值或字符串等類型
數(shù)值
整數(shù)類型有無符號和帶符號兩種既鞠。Go同時支持int和uint,這兩種類型的長度相同盖文,但具體長度取決于不同編譯器的實(shí)現(xiàn)嘱蛋。Go里面也有直接定義好位數(shù)的類型:rune,?int8,?int16,?int32,?int64和byte,?uint8,?uint16,?uint32,?uint64。其中rune是int32的別稱,byte是uint8的別稱洒敏。
注:這些類型的變量之間不允許互相賦值或操作龄恋,不然會在編譯時引起編譯器報錯。另外凶伙,盡管int的長度是32 bit, 但int 與 int32并不可以互用郭毕。浮點(diǎn)數(shù)的類型有float32和float64兩種(沒有float類型),默認(rèn)是float64函荣。
字符串
Go中的字符串都是采用UTF-8字符集編碼显押。字符串是用一對雙引號("")或反引號(`?`)括起來定義,它的類型是string傻挂。
注:在Go中字符串是不可變的乘碑,若一定要修改,可以將字符串 s 轉(zhuǎn)換為 []byte 類型金拒,再轉(zhuǎn)換回 string 類型兽肤,Go中可以使用+操作符來連接兩個字符串,字符串雖不能更改绪抛,但可進(jìn)行切片操作资铡,如果要聲明一個多行的字符串怎么辦?可以通過`來聲明睦疫。
array害驹、slice、map
array就是數(shù)組蛤育,var arr [n]type宛官,長度也是數(shù)組類型的一部分,因此[3]int與[4]int是不同的類型瓦糕,數(shù)組也就不能改變長度底洗。數(shù)組之間的賦值是值的賦值,即當(dāng)把一個數(shù)組作為參數(shù)傳入函數(shù)的時候咕娄,傳入的其實(shí)是該數(shù)組的副本亥揖,而不是它的指針。如果要使用指針圣勒,那么就需要用到后面介紹的slice類型了费变。
“動態(tài)數(shù)組”,在Go里面這種數(shù)據(jù)結(jié)構(gòu)叫slice圣贸,slice并不是真正意義上的動態(tài)數(shù)組挚歧,而是一個引用類型。slice總是指向一個底層array吁峻,slice的聲明也可以像array一樣滑负,只是不需要長度在张。slice可以從一個數(shù)組或一個已經(jīng)存在的slice中再次聲明。slice通過array[i:j]來獲取矮慕,其中i是數(shù)組的開始位置帮匾,j是結(jié)束位置,但不包含array[j]痴鳄,它的長度是j-i瘟斜,類似于python的切片或者c++的vector。
從概念上面來說slice像一個結(jié)構(gòu)體夏跷,這個結(jié)構(gòu)體包含了三個元素:
一個指針哼转,指向數(shù)組中slice指定的開始位置。
長度槽华,即slice的長度。
最大長度趟妥,也就是slice開始位置到數(shù)組的最后位置的長度猫态。
對于slice有幾個有用的內(nèi)置函數(shù):
len?獲取slice的長度
cap?獲取slice的最大容量
append?向slice里面追加一個或者多個元素,然后返回一個和slice一樣類型的slice
copy?函數(shù)copy從源slice的src中復(fù)制元素到目標(biāo)dst披摄,并且返回復(fù)制的元素的個數(shù)
注:append函數(shù)會改變slice所引用的數(shù)組的內(nèi)容亲雪,從而影響到引用同一數(shù)組的其它slice。 但當(dāng)slice中沒有剩余空間(即(cap-len) == 0)時疚膊,此時將動態(tài)分配新的數(shù)組空間义辕。返回的slice數(shù)組指針將指向這個空間,而原數(shù)組的內(nèi)容將保持不變寓盗;其它引用此數(shù)組的slice則不受影響灌砖。
注意:slice和數(shù)組在聲明時的區(qū)別:聲明數(shù)組時,方括號內(nèi)寫明了數(shù)組的長度或使用...自動計算長度傀蚌,而聲明slice時基显,方括號內(nèi)沒有任何字符。slice是引用類型善炫,所以當(dāng)引用改變其中元素的值時撩幽,其它的所有引用都會改變該值。
map類似python中的字典箩艺。聲明舉例:var numbersmap [string]int窜醉,numbers =make(map[string]int)。
使用map過程中需要注意的幾點(diǎn):
map是無序的艺谆,每次打印出來的map都會不一樣榨惰,它不能通過index獲取,而必須通過key獲取
map的長度是不固定的擂涛,也就是和slice一樣读串,也是一種引用類型
內(nèi)置的len函數(shù)同樣適用于map聊记,返回map擁有的key的數(shù)量
map的值可以很方便的修改,通過numbers["one"]=11可以很容易的把key為one的字典值改為11
map和其他基本型別不同恢暖,它不是thread-safe排监,在多個go-routine存取時,必須使用mutex lock機(jī)制
map的初始化可以通過key:val的方式初始化值杰捂,同時map內(nèi)置有判斷是否存在key的方式
通過delete刪除map的元素:
map也是一種引用類型舆床,如果兩個map同時指向一個底層,那么一個改變嫁佳,另一個也相應(yīng)的改變
make挨队、new操作
make用于內(nèi)建類型(map、slice?和channel)的內(nèi)存分配蒿往。new用于各種類型的內(nèi)存分配盛垦。
注:new返回指針。make返回初始化后的(非零)值瓤漏。make 只能為 slice腾夯、map或 channel 類型分配內(nèi)存并初始化,同時返回一個有初始值的 slice蔬充、map 或 channel 類型引用蝶俱,不是指針。內(nèi)建函數(shù) new 用來分配內(nèi)存饥漫,它的第一個參數(shù)是一個類型榨呆,不是一個值,它的返回值是一個指向新分配類型零值的指針庸队。
流程控制
Go里面if條件判斷語句中不需要括號积蜻,Go的if還有一個強(qiáng)大的地方就是條件判斷語句里面允許聲明一個變量,這個變量的作用域只能在該條件邏輯塊內(nèi)皿哨,其他地方就不起作用了浅侨,比如if x=1;x>0{執(zhí)行體}。
goto類似c語言中的goto证膨,
for如输,在循環(huán)里面有兩個關(guān)鍵操作break和continue ,break操作是跳出當(dāng)前循環(huán),continue是跳過本次循環(huán)央勒。當(dāng)嵌套過深的時候不见,break可以配合標(biāo)簽使用,即跳轉(zhuǎn)至標(biāo)簽所指定的位置崔步,break和continue還可以跟著標(biāo)號稳吮,用來跳到多重循環(huán)中的外層循環(huán)。
switch井濒,類似與c的switch灶似,Go里面switch默認(rèn)相當(dāng)于每個case最后帶有break列林,匹配成功后不會自動向下執(zhí)行其他case,而是跳出整個switch酪惭,但是可以使用fallthrough強(qiáng)制執(zhí)行后面的case代碼希痴。
函數(shù)
注:函數(shù)可以返回多個值。Go函數(shù)支持變參春感,如 func myfunc(arg...int) {}砌创。
傳值,當(dāng)我們傳一個參數(shù)值到被調(diào)用函數(shù)里面時鲫懒,實(shí)際上是傳了這個值的一份copy嫩实,當(dāng)在被調(diào)用函數(shù)中修改參數(shù)值的時候,調(diào)用函數(shù)中相應(yīng)實(shí)參不會發(fā)生任何變化窥岩,因?yàn)閿?shù)值變化只作用在copy上甲献。
傳指針使得多個函數(shù)能操作同一個對象。傳指針比較輕量級 (8bytes),只是傳內(nèi)存地址颂翼,我們可以用指針傳遞體積大的結(jié)構(gòu)體竟纳。如果用參數(shù)值傳遞的話, 在每次copy上面就會花費(fèi)相對較多的系統(tǒng)開銷(內(nèi)存和時間)。所以當(dāng)你要傳遞大的結(jié)構(gòu)體的時候疚鲤,用指針是一個明智的選擇。Go語言中channel缘挑,slice集歇,map這三種類型的實(shí)現(xiàn)機(jī)制類似指針,所以可以直接傳遞语淘,而不用取地址后傳遞指針诲宇。(注:若函數(shù)需改變slice的長度,則仍需要取地址傳遞指針)
defer惶翻,Go語言中有種不錯的設(shè)計姑蓝,即延遲(defer)語句,你可以在函數(shù)中添加多個defer語句吕粗。當(dāng)函數(shù)執(zhí)行到最后時纺荧,這些defer語句會按照逆序執(zhí)行,最后該函數(shù)返回颅筋。
函數(shù)作為值宙暇、類型。在Go中函數(shù)也是一種變量议泵,我們可以通過type來定義它占贫,它的類型就是所有擁有相同的參數(shù),相同的返回值的一種類型先口。函數(shù)當(dāng)做值和類型在我們寫一些通用接口的時候非常有用型奥。
Panic和Recover
Panic是一個內(nèi)建函數(shù)瞳收,可以中斷原有的控制流程,進(jìn)入一個panic狀態(tài)中厢汹。當(dāng)函數(shù)F調(diào)用panic螟深,函數(shù)F的執(zhí)行被中斷,但是F中的延遲函數(shù)defer會正常執(zhí)行坑匠,然后F返回到調(diào)用它的地方血崭。在調(diào)用的地方,F(xiàn)的行為就像調(diào)用了panic厘灼。這一過程繼續(xù)向上夹纫,直到發(fā)生panic的goroutine中所有調(diào)用的函數(shù)返回,此時程序退出设凹。panic可以直接調(diào)用panic產(chǎn)生舰讹。也可以由運(yùn)行時錯誤產(chǎn)生,例如訪問越界的數(shù)組闪朱。
Recover是一個內(nèi)建的函數(shù)月匣,可以讓進(jìn)入panic狀態(tài)的goroutine恢復(fù)過來。recover僅在延遲函數(shù)defer中有效奋姿。在正常的執(zhí)行過程中锄开,調(diào)用recover會返回nil,并且沒有其它任何效果称诗。如果當(dāng)前的goroutine陷入panic狀態(tài)萍悴,調(diào)用recover可以捕獲到panic的輸入值,并且恢復(fù)正常的執(zhí)行寓免。
main函數(shù)和init函數(shù)
Go里面有兩個保留的函數(shù):init函數(shù)(能夠應(yīng)用于所有的package)和main函數(shù)(只能應(yīng)用于package main)癣诱。這兩個函數(shù)在定義時不能有任何的參數(shù)和返回值。雖然一個package里面可以寫任意多個init函數(shù)袜香,但這無論是對于可讀性還是以后的可維護(hù)性來說撕予,我們都強(qiáng)烈建議用戶在一個package中每個文件只寫一個init函數(shù)。
Go程序會自動調(diào)用init()和main()蜈首,所以你不需要在任何地方調(diào)用這兩個函數(shù)实抡。每個package中的init函數(shù)都是可選的,但package main就必須包含一個main函數(shù)疾就。
程序的初始化和執(zhí)行都起始于main包澜术。如果main包還導(dǎo)入了其它的包,那么就會在編譯時將它們依次導(dǎo)入猬腰。有時一個包會被多個包同時導(dǎo)入鸟废,那么它只會被導(dǎo)入一次(例如很多包可能都會用到fmt包,但它只會被導(dǎo)入一次姑荷,因?yàn)闆]有必要導(dǎo)入多次)盒延。當(dāng)一個包被導(dǎo)入時缩擂,如果該包還導(dǎo)入了其它的包,那么會先將其它包導(dǎo)入進(jìn)來添寺,然后再對這些包中的包級常量和變量進(jìn)行初始化胯盯,接著執(zhí)行init函數(shù)(如果有的話),依次類推计露。等所有被導(dǎo)入的包都加載完畢了博脑,就會開始對main包中的包級常量和變量進(jìn)行初始化,然后執(zhí)行main包中的init函數(shù)(如果存在的話)票罐,最后執(zhí)行main函數(shù)叉趣。下圖詳細(xì)地解釋了整個執(zhí)行過程:
import這個命令用來導(dǎo)入包文件,類似于include该押,
import . "fmt" 點(diǎn)操作的含義就是這個包導(dǎo)入之后在你調(diào)用這個包的函數(shù)時疗杉,你可以省略前綴的包名
import f “fmt” 別名 fmt.Printf()->f.Printf()
import _ “fmt” _操作其實(shí)是引入該包,而不直接使用包里面的函數(shù)蚕礼,而是調(diào)用了該包里面的init函數(shù)烟具。
struct類型
和C語言類似
定義:
type person struct {
??????? name strin
??????? gage int
}
1.按照順序提供初始化值
P := person{"Tom", 25}
2.通過field:value的方式初始化,這樣可以任意順序
P := person{age:24, name:"Tom"}
3.當(dāng)然也可以通過new函數(shù)分配一個指針奠蹬,此處P的類型為*person
P := new(person)
匿名字段:Go支持只提供類型朝聋,而不寫字段名的方式,也就是匿名字段囤躁,也稱為嵌入字段玖翅。當(dāng)匿名字段是一個struct的時候,那么這個struct所擁有的全部字段都被隱式地引入了當(dāng)前定義的這個struct割以。通過匿名訪問和修改字段相當(dāng)?shù)挠杏茫遣粌H僅是struct字段哦应媚,所有的內(nèi)置類型(int string等)和自定義類型都是可以作為匿名字段的严沥。
注:結(jié)構(gòu)中匿名字段和結(jié)構(gòu)本身字段重名,Go里面很簡單的解決了這個問題中姜,最外層的優(yōu)先訪問
面向?qū)ο?/b>
通俗的說就是給struct綁定了函數(shù)消玄,函數(shù)接收者可以時值copy,也可以是指針丢胚,
method的語法如下:
func (r ReceiverType) funcName(parameters) (results)
在使用method的時候重要注意幾點(diǎn)
雖然method的名字一模一樣翩瓜,但是如果接收者不一樣,那么method就不一樣
method里面可以訪問接收者的字段
調(diào)用method通過.訪問携龟,就像struct里面訪問字段一樣
method 重寫兔跌,繼承。
舉例
package main
import "fmt"
type Human struct {
? ? name? string
? ? age? int
? ? phone string // Human類型擁有的字段
}
type Student struct {
? ? Human
? ? school string
? ? phone? string
}
type Employee struct {
? ? Human? ? ? // 匿名字段Human
? ? speciality string
? ? phone? ? ? string // 雇員的phone字段
}
func (h Human) say() {
? ? fmt.Println(h.name, h.age, h.phone)
}+++
func (h Employee) say() {
? ? fmt.Println(h.name, h.age, h.phone, h.speciality, h.Human.phone)
}
func main() {
? ? Bob := Employee{Human{"Bob", 34, "777-444-XXXX"}, "Designer", "333-222"}
? ? Tom := Student{Human{"Tom", 18, "123-456-189"}, "yichuan", "666"}
? ? Tom.say()
? ? Bob.say()
? ? fmt.Println(Tom.phone, Tom.Human.phone)
注:以上筆記基本來自
https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/preface.md
作為業(yè)余愛好僅僅記錄了一些注意點(diǎn)