1.類型
布爾值,數值與字符串類型的實例的命名是預聲明的。 數組,結構,指針,函數,接口,切片,映射和信道這些復合類型可由類型字面構造。
每個類型?T
?都有一個?基本類型:若?T
?為預聲明類型或類型字面, 其相應的基本類型為?T
?本身。否則,T
的基本類型為其?類型聲明中所依據類型的基本類型。
type T1 stringtype T2 T1type T3 []T1type T4 T3
?
以上 string,T1 和 T2 的基本類型為 string。 []T1,T3 和 T4 的基本類型為 []T1 。
2.類型與值
注意下面說的是類型相同
-
- 若兩個數組類型其元素類型相同且長度相同,那么它們的類型相同。
- 若兩個切片類型其元素類型相同,那么它們的類型相同。
- 若兩個結構類型其字段序列相同,相應字段名相同,類型相同,標注相同,那么它們的類型相同。 兩個匿名字段其名字被認為相同。出自不同包的小寫字段名總不相同。
- 若兩個指針類型其基礎類型相同,那么它們的類型相同。
- 若兩個函數類型其形參個數相同,返回值相同,相應形參類型相同,返回值類型相同, 兩函數都可變或都不可變,那么它們的類型相同。形參和返回值名無需匹配。
- 若兩個接口類型其方法集相同,名字相同,函數類型相同,那么它們的類型相同。 出自不同包的小寫方法名總不相同。兩接口類型是否相同與方法的次序無關。
- 若兩個映射類型其鍵值類型相同,那么它們的類型相同。
- 若兩個信道類型其值類型相同,方向相同,那么它們的類型相同。
3.可賦值性
-
- 當?
x
?的類型和?T
?相同時。 - 當?
x
?的類型?V
?和?T
?有相同的?基本類型?且在?V
?或?T
?中至少有一個不是已命名類型時。 - 當?
T
?為接口類型且?x
?實現了?T
時。 - 當?
x
?為雙向信道值、T
?為信道類型、?x
?的類型?V
?和?T
?的元素類型相同且在?V
?或?T
?中至少有一個不是已命名類型時。 - 當?
x
?為預聲明標識符?nil
?且?T
?為指針、函數、切片、映射、通道或接口類型時。 - 當?
x
?為無類型化,可通過類型?T
?的值來表示的?常量時。
- 當?
任何類型都可賦予空白標識符.
4.布爾類型 bool
布爾類型 表示由預聲明常量 true 和 false所代表的布爾值的集。 預聲明的布爾類型為 bool。
5.數值類型 int等
uint8 所有無符號 8位整數集(0 到 255) uint16 所有無符號16位整數集(0 到 65535) uint32 所有無符號32位整數集(0 到 4294967295) uint64 所有無符號64位整數集(0 到 18446744073709551615)int8 所有帶符號 8位整數集(-128 到 127) int16 所有帶符號16位整數集(-32768 到 32767) int32 所有帶符號32位整數集(-2147483648 到 2147483647) int64 所有帶符號64位整數集(-9223372036854775808 到 9223372036854775807)float32 所有IEEE-754 32位浮點數集 float64 所有IEEE-754 64位浮點數集complex64 所有帶float32實部和虛部的復數集 complex128 所有帶float64實部和虛部的復數集byte uint8的別名 rune int32的別名
除 byte 為 uint8 的別名以及 rune 為 int32 的別名外,所有數值類型都是不同的。 當不同的數值類型混合在一個表達式或賦值操作中時,必須進行類型轉換。 例如,int32 與 int 是不同的類型, 盡管它們在特定架構上可能有相同的大小。
大小取決于具體實現的預聲明數值類型:
uint 32或64位 int 大小與uint相同 uintptr 大到足以存儲指針值無解釋位的無符號整數
6.字符串類型 string
字符串是不可變的: 一旦被創建,字符串的內容就不能更改。
字符串 s 的長度(即其字節大小)可使用內建函數 len 獲取。若該字符串為常量,則其長度即為編譯時常量。
字符串的字節可通過整數0 至 len(s)-1 訪問。
獲取這樣一個元素的地址是非法的;若 s[i] 為字符串的第 i 個字節,&s[i] 就是無效的。
x := "ssssssss"var str interface{} = x[0]b := str.(byte) //斷言是否是字節.如果是則b為該值,如果不是則恐慌fmt.Println(b)
?
7.數組類型
數組 a 的長度可使用內建函數 len獲取, 其元素可通過整數下標 0 到 len(a)-1 尋址。 數組類型總是一維的,但可組合構成多維的類型。
[32]byte [2*N] struct { x, y int32 } [1000]*float64 [3][5]int [2][2][2]float64 // 等價于[2]([2]([2]float64)) a := [3]int{1, 2, 3} // 聲明了一個長度為3的int數組 b := [10]int{1, 2, 3} // 聲明了一個長度為10的int數組,其中前三個元素初始化為1、2、3,其它默認為0 c := [...]int{4, 5, 6} // 可以省略長度而采用`...`的方式,Go會自動根據元素個數來計算長度
?
8.切片類型 slice
類似于數組,切片是可索引的且擁有一個長度。切片 s 的長度可通過內建函數 len獲取;不同于數組的是,切片可在執行過程中被改變, 其元素可通過整數0 到 len(s)-1 尋址。 給定元素的切片下標可能小于它在其基本數組中的下標。
容量 是該擴展的量度: 它是切片的長度和切片往后數組的長度之和;長度達到其容量的切片可通過從原切片 ‘切下’一個新的來創建。 切片 a 的容量可使用內建函數 cap(a) 獲取。
var sli2 []int sli2 = make([]int, 20, 30)sli2[5] = 10fmt.Println(sli2)sli := make([]int, 5, 10)sli[4] = 5fmt.Println(sli)sli3 := []int{5, 2}fmt.Println(sli3)
?
產生切片與分配數組后再對其進行切片相同,因此這兩個例子的結果為相同的切片:
make([]int, 50, 100)new([100]int)[0:50]
切片是引用的
9.結構類型
通過有類型而無顯式字段名聲明的字段為 匿名字段,亦稱為 嵌入式 字段或該結構中此種類型的嵌入。 這種字段類型必須作為一個類型名 T 或一個非接口類型名的指針 *T來實現, 且 T 本身不能為指針類型。
// 帶類型為T1,*T2,P.T3和*P.T4的4個匿名字段的結構 struct {T1 // 字段名為T1*T2 // 字段名為T2P.T3 // 字段名為T3*P.T4 // 字段名為T4x, y int // 字段名為x和y }
以下為非法聲明,因為字段名在結構類型中必須是唯一的:
struct {T // 與匿名字段*T及*P.T相沖突*T // 與匿名字段T及*P.T相沖突*P.T // 與匿名字段T及*T相沖突 }
在結構 x 中,若 x.f 為表示字段或方法 f 的合法選擇者,則匿名字段的字段或方法 f 即為已提升的。
給定結構類型?S
?與名為?T
?的類型,包含在結構方法集中的已提升方法如下:
-
- 若?
S
?包含一個匿名字段?T
,則?S
?與?*S
?的方法集均包含帶接收者?T
?的已提升方法。*S
?的方法集也包含帶接收者?*T
?的已提升方法。 - 若?
S
?包含匿名字段?*T
,則?S
?與?*S
?的方法集均包含帶接收者?T
?或?*T
?的已提升方法。
- 若?
字段聲明可后跟一個可選的字符串字面 標注,成為所有相應字段聲明中字段的屬性。 標注可通過 反射接口 獲得,否則就會被忽略。
// 一個對應于時間戳協議緩存的結構. // 標注字符串定義了協議緩存的字段號. struct {microsec uint64 "field 1"serverIP6 uint64 "field 2"process string "field 3" }
10.指針類型
指針類型表示一個所有給定類型變量的指針的集,稱為指針的 基礎類型。 未初始化的指針的值為 nil。
x:=3y:=&x //y這時變成了指針,傳x的地址fmt.Println(y, *y) //*y該地址的值
注意:上面的*y中的*在表達式中代表該指針的值,在類型定義中,則代表某類型變量的指針
type strPoint *stringfunc main() {var strP strPointi := "a"strP = &ifmt.Println(strP) }
11.函數類型 func
在Go中函數也是一種變量,我們可以通過type來定義它,它的類型就是所有擁有相同的參數,相同的返回值的一種類型
type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])type testInt func(int) bool // 聲明了一個函數類型 func a(integer int) bool {if integer%2 == 0 {return false}return true }func b(integer int) bool {if integer%2 == 0 {return true}return false }func filter(slice []int, f testInt) []int { // 聲明的函數類型在這個地方當做了一個參數,這里即可以傳a,也可以傳bvar result []intfor _, value := range slice {if f(value) {result = append(result, value)}}return result }
?
函數當做值和類型在我們寫一些通用接口的時候非常有用,通過上面例子我們看到testInt這個類型是一個函數類型,然后兩個filter函數的參數和返回值與testInt類型是一樣的,但是我們可以實現很多種的邏輯,這樣使得我們的程序變得非常的靈活。
函數簽名中的最后一個形參可能有一個帶 ... 前綴的類型。 帶這樣形參的函數被稱為 變參函數 它可接受零個或多個實參的函數。
func() func(x int) int func(a, _ int, z float32) bool func(a, b int, z float32) (bool) func(prefix string, values ...int) func(a, b int, z float64, opt ...interface{}) (success bool) //for _, n := range arg 來循環參數 func(int, int, float64) (float64, *[]int) func(n int) func(p *T)
12.接口類型 interface
接口類型指定一個稱為 接口 的 方法集。 接口類型變量可存儲任何帶方法集類型的值,該方法集為此接口的超集。 這種類型表示 實現此接口。未初始化的接口類型變量的值為 nil。
空interface(interface{})不包含任何的method,正因為如此,所有的類型都實現了空interface。空interface對于描述起不到任何的作用(因為它不包含任何的method),但是空interface在我們需要存儲任意類型的數值的時候相當有用,因為它可以存儲任意類型的數值。
// 定義a為空接口 var a interface{} var i int = 5 s := "Hello world" // a可以存儲任意類型的數值 a = i a = s
?
一個函數把interface{}作為參數,那么他可以接受任意類型的值作為參數,如果一個函數返回interface{},那么也就可以返回任意類型的值。是不是很有用啊!
13.映射類型 map
映射通過另一類型唯一的 鍵 集索引,該類型稱為鍵類型。 未初始化的映射值為 nil。
比較操作符 == 和 != 必須由鍵類型的操作數完全定義; 因此鍵類型不能是函數,映射或切片。若該鍵類型為接口類型,這些比較運算符必須由動態鍵值定義; 失敗將導致一個 運行時恐慌.
元素的數量稱為長度。 對于映射 m,其長度可使用內建函數 len 獲取并可在執行時更改。元素可在執行時使用賦值來添加并通過 下標表達式 來檢索;它們也可通過內建函數 delete 刪除。
var numbers map[string] intnumbers = make(map[string] int) //注意是=號numbers["ss"]=5m := make(map[string]string)m["Hello"] = "Bonjour"rating := map[string]float32{"C": 5, "Go": 4.5, "Python": 4.5, "C++": 2}fmt.Println(rating)
map也是一種引用類型,如果兩個map同時指向一個底層,那么一個改變,另一個也相應的改變:
14.信道類型 chan
信道提供一種機制使兩個并發執行的函數同步執行,并通過傳遞具體元素類型的值來通信。 未初始化的信道值為 nil。
信道雖然使用make創建,但不是引用的.確保在并發過程中,各個并發的程序(返回值)能夠和父環境通信
容量根據元素的數量設置信道中緩存的大小。若容量大于零,則信道是異步的:?
若緩存未滿(發送)或非空(接收),則通信操作無阻塞成功,且元素在發送序列中被接收。?
若容量為零或無,則只有當發送者和接收者都做好準備時通信才會成功。 nil 信道永遠不會準備好通信。
信道可通過內建函數close關閉; 接收操作符的多值賦值形式可測試信道是否關閉。
ci := make(chan int) cs := make(chan string) cf := make(chan interface{})
channel通過操作符<-
來接收和發送數據
ch <- v // 發送v到channel ch. v := <-ch // 從ch中接收數據,并賦值給v
舉個例子:
package mainimport "fmt"func sum(a []int, c chan int) {sum := 0for _, v := range a {sum += v}c <- sum // send sum to c }func main() {a := []int{7, 2, 8, -9, 4, 0}c := make(chan int)go sum(a[:len(a)/2], c)go sum(a[len(a)/2:], c)x, y := <-c, <-c // receive from c fmt.Println(x, y, x + y) }
?
默認情況下,channel接收和發送數據都是阻塞的,除非另一端已經準備好,這樣就使得Goroutines同步變的更加的簡單,而不需要顯式的lock。所謂阻塞,也就是如果讀取(value := <-ch)它將會被阻塞,直到有數據接收。其次,任何發送(ch<-5)將會被阻塞,直到數據被讀出。無緩沖channel是在多個goroutine之間同步很棒的工具。
<-
操作符指定信道的 方向,發送 或 接收。 若沒有給定方向,那么該信道就是 雙向的。 信道可通過類型轉換 或 賦值被強制為只發送或只接收。
chan T // 可以被用來發送和接收類型T的值 chan<- float64 // 只能被用來發送浮點數 <-chan int // 只能被用來接收整數
<-
?操作符結合最左邊的?chan
?可能的方式:
chan<- chan int // 等價于 chan<- (chan int) chan<- <-chan int // 等價于 chan<- (<-chan int) <-chan <-chan int // 等價于 <-chan (<-chan int) chan (<-chan int)