這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
一、常量:
const Pi float64 = 3.14159265358979323846
const zero = 0.0 // 無類型浮點常量
const (
size int64 = 1024
eof = -1 // 無類型整型常量
)
const u, v float32 = 0, 3 // u = 0.0, v = 3.0,常量的多重賦值
const a, b, c = 3, 4, "foo"
二、Go語言預定義了這些常量:【true】、【false】和【iota】。
iota比較特殊,可以被認為是一個可被編譯器修改的常量,在每一個const關鍵字出現時被重設為0,然後在下一個const出現之前,每出現一次iota,其所代表的數字會自動增1。
從以下的例子可以基本理解iota的用法:
const ( // iota被重設為0
c0 = iota // c0 == 0
c1 = iota // c1 == 1
c2 = iota // c2 == 2
)
三、枚舉:
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
numberOfDays // 這個常量沒有匯出, 因為他是小寫開頭, 僅包內可見
)
四、整型: 既然整型有平台相關性, 還是少用int uint好一些。
類 型 長度(位元組) 值 範 圍
int8 1 ?128 ~ 127
uint8(即byte) 1 0 ~ 255
int16 2 ?32 768 ~ 32 767
uint16 2 0 ~ 65 535
int32 4 ?2 147 483 648 ~ 2 147 483 647
uint32 4 0 ~ 4 294 967 295
int64 8 ?9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807
uint64 8 0 ~ 18 446 744 073 709 551 615
int 平台相關 平台相關
uint 平台相關 平台相關
uintptr 同指標 在32位平台下為4位元組,64位平台下為8位元組
五、浮點型:
Go語言定義了兩個類型float32和float64,其中float32等價於C語言的float類型,float64等價於C語言的double類型。
在Go語言裡,定義一個浮點數變數的代碼如下:
var fvalue1 float32
fvalue1 = 12
fvalue2 := 12.0 // 如果不加小數點,fvalue2會被推導為整型而不是浮點型
浮點數比較:
因為浮點數不是一種精確的表達方式,所以像整型那樣直接用==來判斷兩個浮點數是否相等是不可行的,這可能會導致不穩定的結果。下面是一種推薦的替代方案:
import "math"
// p為使用者自訂的比較精度,比如0.00001
func IsEqual(f1, f2, p float64) bool {
return math.Fdim(f1, f2) < p
}
六、【字串】
聲明: var str string
str = “hello”
或者: str := “hello”
操作: ch := str[0] // 取字串的第一個字元
字串的內容不能在初始化後被修改: str[0] = 'X' // 編譯錯誤
x + y 字串串連 "Hello" + "123" // 結果為Hello123
len(s) 字串長度 len("Hello") // 結果為5
s[i] 取字元 "Hello" [1] // 結果為'e'
更多的字串操作,golang標準庫strings包。
七、數組:
聲明:以下為一些常規的數組聲明方法:
[32]byte // 長度為32的數組,每個元素為一個位元組
[2*N] struct { x, y int32 } // 複雜類型數組
[1000]*float64 // 指標數組
[3][5]int // 二維數組
[2][2][2]float64 // 等同於[2]([2]([2]float64))
迴圈:
for i := 0; i < len(array); i++ {
fmt.Println("Element", i, "of array is", array[i])
}
for i, v := range array {
fmt.Println("Array element[", i, "]=", v)
}
【★ 實值型別】所有的實值型別變數在賦值和作為參數傳遞時都將產生一次複製動作。如果將數組作為函數的參數類型,則在函數調用時該參數將發生資料複製。
需要特別注意的是,在Go語言中數組是一個實值型別(value type)。因此,在函數體中無法修改傳入的數組的內容,因為函數內操作的只是所
傳入數組的一個副本。
八、數組切片(slice)
聲明:
基於數組建立一個數組切片 var mySlice []int = myArray[:5]
基於myArray的所有元素建立數組切片: mySlice = myArray[:]
建立一個初始元素個數為5的數組切片,元素初始值為0: mySlice1 := make([]int, 5)
建立一個初始元素個數為5的數組切片,元素初始值為0,並預留10個元素的儲存空間:mySlice2 := make([]int, 5, 10)
直接建立並初始化包含5個元素的數組切片: mySlice3 := []int{1, 2, 3, 4, 5}
數組切片支援Go語言內建的cap()函數和len()函數,代碼清單2-2簡單示範了這兩個內建函數的用法。
可以看出,cap()函數返回的是數組切片分配的空間大小,而len()函數返回的是數組切片中當前所儲存的元素個數。
添加元素:
mySlice = append(mySlice, 1, 2, 3)
mySlice2 := []int{8, 9, 10}
// 給mySlice後面添加另一個數組切片
mySlice = append(mySlice, mySlice2...) 。注意需要三個點...
複製數組切片:slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1) // 只會複製slice1的前3個元素到slice2中
copy(slice1, slice2) // 只會複製slice2的3個元素到slice1的前3個位置
【引用傳遞】 數組切片是引用傳遞。
九、map
聲明:var personDB map[string] PersonInfo
personDB = make(map[string] PersonInfo)
// 往這個map裡插入幾條資料
personDB["12345"] = PersonInfo{"12345", "Tom", "Room 203,..."}
personDB["1"] = PersonInfo{"1", "Jack", "Room 101,..."}
// 從這個map尋找鍵為"1234"的資訊
person, ok := personDB["1234"]
// ok是一個返回的bool型,返回true表示找到了對應的資料
if ok {
fmt.Println("Found person", person.Name, "with ID 1234.")
} else {
fmt.Println("Did not find person with ID 1234.")
}
建立了一個初始儲存能力為100的map: myMap = make(map[string] PersonInfo, 100)
賦值 : myMap["1234"] = PersonInfo{"1", "Jack", "Room 101,..."}
刪除 : delete(myMap, "1234")
尋找 : value, ok := myMap["1234"]
if ok { // 找到了
// 處理找到的value
}
【引用傳遞】 map是引用傳遞。
【★★★ 引用語義】 golang中的引用語義類型有: 數組切片、map、channel、interface。
struct(類)對象往往通過new建立出來,這時會是一個引用傳遞,new出來的本身就是一個指標。
直接聲明struct得到的變數傳遞時是按值傳遞。★★★★★
十、 結構體struct
定義新類型: type Integer int : 定義個Integer 類型, 與int相同。
type Header map[string][]string
結構體:
type Rect struct {
x, y float64
width, height float64
}
func (r *Rect) Area() float64 {
return r.width * r.height
}
執行個體化對象:
rect1 := new(Rect)
rect2 := &Rect{}
rect3 := &Rect{0, 0, 100, 200}
rect4 := &Rect{width: 100, height: 200}
也可以rect5 := Rect{}.
加&符號和new的是指標對象,沒有的則是值對象,這點和php、java不一致,在傳遞對象的時候要根據實際情況來決定是要傳遞指標還是值。
現在看還是用引用聲明方式好一點。
建構函式: 查看官方文檔,golang並沒有建構函式一說。如果一定要在初始化對象的時候進行一些工作的話,可以自行封裝產生執行個體的方法。
func NewPoem(param string, p ...interface{}) *Poem
樣本:
func NewPoem(author string) (poem *Poem) {
poem = &Poem{}
poem.Author = author
return
}
struct是組合。
【TIPS】: 傳統的繼承,有兩方面的作用: 一是複用,即子類複用父類的一部分方法,自己再添加一些特殊的。 二是表示,即父類提供
一個通用的表示方式,不同的子類去實現,這種也很常見比如抽象類別。
那麼第一種應該完全的切換到組合的思考方式, 而第二種則通過interface實現。 java已經沒有了virtual關鍵字,引入了interface,go也保留了
interface,解決了第二個問題。 第一個go直接通過組合實現。
十一、 介面 interface
在Go語言出現之前,介面主要作為不同組件之間的契約存在。對契約的實現是強制的,你必須聲明你的確實現了該介面。
為了實現一個介面,你需要從該介面繼承。
在Go語言中,一個類只需要實現了介面要求的所有函數,我們就說這個類實現了該介面。 就可以賦值給這個介面。
【介面賦值】: var b LessAdder = &a ... (1) var b LessAdder = a ... (2) 應該用1的寫法, 2會有問題。
【介面查詢】:
var file1 Writer = ...
if file5, ok := file1.(two.IStream); ok {
...
}
這個if語句檢查file1介面指向的對象執行個體是否實現了two.IStream介面,如果實現了,則執行特定的代碼。
在Go語言中,你可以詢問介面它指向的對象是否是某個類型,比如:
var file1 Writer = ...
if file6, ok := file1.(*File); ok {
...
}
這個if語句判斷file1介面指向的對象執行個體是否是*File類型,如果是則執行特定代碼。
【類型查詢】:
var v1 interface{} = ...
switch v := v1.(type) {
case int: // 現在v的類型是int
case string: // 現在v的類型是string
...
}
介面可以給介面賦值。 介面是引用傳遞。
-------------------------------------------
var i itest |
sp := new(stest) |
i = sp
fmt.Println("point i", unsafe.Sizeof(i)) |
fmt.Println("point s", unsafe.Sizeof(sp)) |
i.do() |
ss := stest{}
i = ss |
fmt.Println("struct i", unsafe.Sizeof(i)) |
fmt.Println("struct s", unsafe.Sizeof(ss)) |
輸出:
point i 8 |
point s 4 |
struct i 8
struct s 40
可見: 介面類型永遠大小是8,不管賦值給他的是new出來的指標還是結構體,都是可以的。 |結構體的指標大小是4。
-------------------------------------------
十二、 channel :
聲明:var chanName chan ElementType。 如var ch chan int 或者,我們聲明一個map,元素是bool型的channel:var m map[string] chan bool
定義:定義一個channel也很簡單,直接使用內建的函數make()即可: ch := make(chan int)
要建立一個帶緩衝的channel,其實也非常容易: c := make(chan int, 1024)
在調用make()時將緩衝區大小作為第二個參數傳入即可,比如上面這個例子就建立了一個大小為1024的int類型channel,
即使沒有讀取方,寫入方也可以一直往channel裡寫入,在緩衝區被填完之前都不會阻塞。
單向channel:
var ch1 chan int // ch1是一個正常的channel,不是單向的
var ch2 chan<- float64// ch2是單向channel,只用於寫float64資料
var ch3 <-chan int // ch3是單向channel,只用於讀取int資料
應該比較適合用於函數參數中。
關閉: close(ch)。
x, ok := <-ch 這個用法與map中的按鍵擷取value的過程比較類似,只需要看第二個bool傳回值即可,如果傳回值是false則表示ch已經被關閉。
【引用傳遞】 channel是引用傳遞。