變量
變量聲明
var 變量名 變量類型
變量初始化
var v1 int = 10
var v2 = 10
v3 := 10
出現在冒號左側的不應該是被聲明過的變量,而且這種簡短聲明只能出現在函數中
變量賦值
可以採用多重賦值 i, j = x, y
匿名變量
函數返回多個值,如果值想獲得其中的某些可以使用 __, __, nickname := GetName()
常量
字面常量
硬編碼的常量,不用像c語言一樣在末尾加上L來區別是int還是long,go中字面常量是無類型的,只要這個常量在相應類型值域範圍內,就可作爲該類型常量
常量定義
通過const關鍵字來定義
也可以是無類型的
編譯期確定,所以右值不能是執行期才確定值的表達式
預定義常量
true,false,iota
itoa在每一個const關鍵字出現時被置爲0,下一個const出現之前,每出現一次iota,其代表的數字會自動加1
枚舉
通過const後緊跟一對圓括號定義一組常量,而沒有關鍵字enum
const (
Sunday = iota
Monday
...
)
同Go語言的其他符號一樣,以大寫字母開頭的常量在包外可見
類型
基礎類型
- 布爾類型 bool
- 整形 int8 byte int16 int uint unitptr等
- 浮點類型 float32,float64
- 複數類型 complex64 complex128
- 字符串 string
- 字符類型 rune
- 錯誤類型 eror
複合類型
- 指針
- 數組
- 切片
- 字典
- 通道
- 結構體
- 接口
布爾
不支持其他類型賦值,不能自動或強制類型轉換
整型
- int uint uintptr這三個是平臺相關的
- int和int32是不同類型,編譯器不會自動轉換,可以強制類型轉換
- 不同類型的值不能進行比較
- 但是各種類型的整型都可以與字面常量比較
- 爲運算中的取反,在c中使用~,在go中使用^
- float32等價與c中float,float64等價與c中的double
- 沒有指定類型時浮點數被自動推導爲float64
- 浮點數的比較不能用==,這可能導致不穩定結果,使用math.Fdim函數來進行比較
複數類型
- complex64由兩個float32構成
- complex128由兩個float64構成
- 不指定類型,而推導出來的是complex128
- value1 = 3.2 + 12i 這樣就可以表示一個複數
- 也可以用complex(3.2, 12)來表示一個複數
- 使用內置函數real(z)獲取實部,使用imag(z)獲取虛部
字符串
c/c++中不存在原生字符串,而是使用字符數組,通過字符指針來傳遞
var str1 string聲明一個字符串
可以像數組一樣用下標訪問,但是初始化之後的字符串不能修改
字符串中包含非ASNI的字符,比如中文,則必須把源碼保存爲UTF-8格式
連接操作 x + y
獲取長度 len(s)
取字符 s[i]
for i : =0; i < n; i++ {
} 以字節數組的方式遍歷
for i, ch := range str {
} 以unicode字符遍歷,此時ch類型爲rune
聲明多行字符串時使用"`",它沒有字符轉義,換行也將輸出
字符類型
byte代表utf-8中單個字節,rune代表unicode中單個字符 出於簡化的考慮,多少api假設字符串爲utf-8編碼,使用unicode的較少
數組
[2*N]struct {x, y int32} 類型數組
[1000]*float64 指針數組
[3][5]二維數組
數組長度定義後不可修改,聲明時可以使用常量或者常量表達式
可以使用...省略號聲明一個數組,go語言會自動根據元素個數計算長度,但是中括號不能爲空,那樣就是slice了
len()獲取長度
可以通過下標訪問
也可以通過for i, v := range array {} 的方式訪問
數組是一個值類型,傳遞參數時會發生一次拷貝,函數中使用的也是副本
數組切片
三組切片的數據結構包含三個變量:一個指向原生數組的指針,數組切片中的元素個數,切片已分配的存儲空間
slice並不是真正的動態數組,而是一個引用類型
基於數據創建切片 myArrary[first:last],基於數組全部創建切片 myArray[:]
直接創建 mySlice1 := make([]int, 5, 10) 創建初始元素個數爲5,元素初始值 0,並預留10個存儲空間
mySlice3 := [int{1,2,3,4,5}] 直接初始化5個元素的數組切片,會有一個匿名的數組被創建
for i := 0; i < len(myArray); i++ 逐一遍歷
或者使用range關鍵字 for i, v := range mySlice 遍歷
cap() 返回切片空間大小, len()返回元素個數
mySlice = append(mySlice, 1,2,3) 尾部追加,並生成新的切片
mySlice = append(mySlice, mySlice2...) 省略號是必須的,append語義是從第二個參數開始是追加的元素,省略號的作用是把 mySlice2所有元素打散傳入
newSlice := oldSlice[:] 基於oldSlice創建新的splice 選擇的範圍不能超過oldslice的cap()值,超過len的部分填充爲0
通過copy來複制一個slice到另一個slice,兩個不一樣大小,以較小的元素個數進行復制
append的函數會改變slice所引用的數組內容,從而影響引用同一數組的其他slice。但當slice中沒有剩餘空間時,此時將動態分配新的數組空間。返回的slice數組指針指向這個空間,而原數組的內容將保持不變,其他引用此數組的slice不受影響。
map
聲明一個map :var 變量名 map[key類型] value類型,這種聲明方式需要在使用之前通過make初始化
map的key可以是所有完全定義了==或者!=操作的類型
可以通過make動態創建 myMap = make(map[key類型] value類型)
創建時初始化 myMap = map[string] PersonInfo { "1234" : PersonInfo{}, }
myMap["1234"] = PersonInfo{} 賦值
元素刪除 delete(myMap, "1234") 元素不存在不會有什麼問題
value, ok := myMap["1234"]
if ok {
} 這種方式進行查找
map是無序的,每次打印的map都會不一樣,,只能通過key獲取,不能通過index獲取
map長度是不固定的,和slice一樣,也是引用類型
len函數返回map擁有的key的數量
make和new的區別
make只能創建slice、map和channel,並且返回一個有 初始值(非零)的T類型,而不是*T。
new分配了零值填充的T類型的內存空間,並且返回其地址,即一個*T類型的值。
這是因爲指向數據結構的引用在使用前必須被初始化,例如slice使用前必須初始化他引用的數組。
問題:通過下標返回的值是拷貝,還是引用?
流程控制
- 條件語句 if、 else 和else if
- 選擇語句 switch、case和select
- 循環語句 for和range
- 跳轉語句 goto
條件語句
條件不需要小括號
無論語句體內有幾條語句,花括號是必須的
左花括號必須與if或else同一行
if之後,條件語句之前,可以添加變量初始化語句,使用分號(;) 分割
在有返回值的函數中,不允許在if else中return,go編譯器找不到終止該函數的return語句
錯誤的例子:
func example(x int) int {
if x == 0 {
return 5
} else{
return x
}
}
選擇語句
左花括號必須和switch同一行
條件表達式不限制爲常量或者整數
單個case中,惡意出現多個結果選項
不需要break來明確退出一個case
只有在case中明確添加fallthrough纔會繼續執行下一個case
可以不設定switch之後的表達式,整個switch和多個if else的邏輯作用相同
循環語句
左花括號必須和for同一行
可以在條件表達式中初始化變量或賦值,多個變量賦值時,只允許使用平行賦值的方式(i,j,k := 1, 2, 3而不是i := 1, j := 2, k := 3)
支持continue和break, break有更高級的用法,可以決定跳出哪曾循環
跳轉語句
goto 跳轉到某個標籤
函數
函數基本組成:關鍵字func,函數名,參數列表,返回值,函數體和返回語句
參數列表和返回值中,多個變量類型相同可以合併,例如func Add(a,b int)
通過import導入函數所在的包,小寫字母開頭的函數只在本包中可見,大寫字母開頭的函數才能被其他包使用
通過(...type) 傳遞不定參數,通過for _, arg := range args 來提取不定參數
函數中調用了其他不定參數的函數,可以原樣傳遞參數,也可以使用切片
func myfunc(args ...int) {
myfunc3(args ...)
myfunc3(args[1:]...)
}
要傳遞任意類型的不定參數,只要將之前的type替換爲interface{}
func Printf(format string, args ...interface{}) { }
通過arg.(type)獲取參數的類型:
func MyPrintf(args ...interface{}) {
for _, arg := range args {
switch arg.(type) {
case int:
case string:
case int64:
default:
}
}
}
多返回值,返回值可以命名,在函數開始的時候初始化爲空,在return不帶任何參數時,會返回對應的返回值變量的值(類似於傳出參數),多個返回值之間用“,”分割,如果函數是首字母大寫的,那麼建議命名返回值變量,這樣有利於生成我文檔
傳遞指針,注意string、slice、map這三種類型的實現類似指針,可以直接傳遞,但函數如果修改slice長度,則仍需傳遞slice指針
不關注某個返回值,可以使用下劃線來跳過這個返回值
函數可以通過type typeName func(input intputType...)(resutlt resultType)的方式來定義類型
匿名函數可以像變量一樣傳遞,
匿名函數也可以直接調用,在函數體最後的右花括號後面緊跟參數列表
閉包
基本概念:閉包是可以包含自由變量的代碼快,這些變量不再這個代碼快內或者任何全局上下文中定義,而在定義代碼快的環境中定義。要執行的代碼快(由於自由變量包含在代碼快中,所以這些自由變量以及她們引用的對象沒有
被釋放)爲自由變量提供綁定的計算環境
價值:函數作爲對象,保存在變量中,通過參數傳遞給其他函數
我的理解,類似與C++中的仿函數對象,只是不需要命名,在外部函數中定義變量,調用外部函數返回內部函數,內部函數訪問了外部函數中定義的變量,因爲有引用所以不能被回收,
錯誤處理
error接口
type error interface {
Error() string
}
自定義error類型
type PathError struct {
Op string
Path string
Err error
}
實現Error方法
func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error()
}
defer 語句遵循先進後出的原則,最後定義的defer先被執行
func panic(interface()) 正常流程終止,執行defer語句,執行完畢後返回到調用函數,並逐層向上執行panic,直至所屬goroutine中所有正在執行的含數據終止
func recover() interface() 用於終止錯誤處理流程,一般應該在defer中調用,截取錯誤處理流程。在未發生一場的goroutine中明確調用回覆過程,會導致該gorutinue所屬的進程打印影廠信息後直接退出
panic作爲最後的手段,代碼中應該沒有或者少有panic
mian和init函數
init函數可用於所有package,main只能用於package main
建議每個package僅使用一個init函數,儘管可以使用多個,但是從可讀性角度來說使用一個i
init和main由go自動調用
init是可選的,但是main是必須的
程序初始化和執行起始於main包,編譯時依次導入依賴包,並初始化包級常量和變量,調用包的init函數,每個包之後被導入一次,等所有導入的包都加載完畢,初始化main包中的常量和變量,調用main的init函數,最後執行main函數
import
可以使用相對路徑和絕對路徑,絕對路徑從gopath/src之後的路徑開始
點操作
import (
. "fmt"
)
這種方式導入包,使用時可以省略包名
別名操作
import (
f "fmt"
)
使用時可以用別名代替包名
_操作
import (
_ "githup.com/ziutek/mymysql/godrv"
)
引入該包,不直接使用包裏的函數,而是調用了該包的init函數