通道(channel)、映射(map)和切片(slice)是引用類型。引用類型的對象須要使用make函數進行構造。git
在Go程序中,若是函數main()返回,整個程序就終止了。這時,Go會關閉所有goroutine。github
使用for range迭代切片時,每次迭代前會返回兩個值:元素在切片中的索引和元素副本。web
Go支持閉包。json
解析JSON示例:數組
type Feed struct { Name string `json:"site"` URI string `json:"link"` Type string `json:"type"` } file, _ := os.Open(filename) var feeds []*Feed json.NewDecoder(file).Decode(&feeds)
聲明接口示例:網絡
type Matcher interface { Search(feed *Feed, searchTerm string) ([]*Result, error) }
使用指針做爲接受者聲明的方法,只能由指針調用。使用值做爲接受者聲明的方法,值和指針均可以調用。當使用值調用時,傳入方法的是值的副本。閉包
若是使用for range對通道進行迭代時,當通道關閉後,迭代會終止。併發
除了main包外,Go包應當與其所在目錄同名。app
Go在尋找包時,先從$GOROOT目錄下尋找,接着在$GOPATH目錄下尋找。函數
命名導入:
import myfmt "mylib/fmt" import _ "mylib/init"
包的初始化。每一個包能夠包含多個init函數,這些函數將在main.main()以前執行。
構建:
go build hello.go # 構建指定文件。 go build # 構建當前目錄。 go build github.com/goinaction/code/chapter3/wordcount # 構建指定包。 go build github.com/goinaction/code/chapter3/... # 構建指定目錄下的所有包。
清理構建文件:
go clean hello.go
構建並執行:
go run hello.go
檢查代碼中的常見錯誤:
go vet go vet main.go go vet .
格式化代碼:
go fmt gofmt -l -w -s .
查看文檔:
go doc tar godoc -http=:6060
函數文檔示例:
// Retrieve 鏈接到配置庫,收集各類連接設置、用戶名和密碼。這個函數成功時 // 返回 config 結構,不然返回一個錯誤。 func Retrieve() (config, error) { // ... }
包文檔示例:
// 包 usb 提供了用於調用 USB 設備的類型和函數。 package usb // ...
聲明數組:
var a1 [5]int var a2 = [3]int{1, 2, 3} var a3 = [...]int{1, 2, 3} var a4 = [3]*int{0: new(int), 1: new(int)}
數組賦值會複製元素:
a1 := [3]string{"a", "b", "c"} var a2 [3]string a2 = a1
多維數組:
var a1 [4][2]int a2 := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}} a3 := [4][2]int{1: {0: 20}, 3: {1: 41}} var a4 [2]int = a3[1]
不要用數組做爲函數參數。這麼作會複製大量對象。要使用切片。
創建切片:
s1 := make([]int, 5) s2 := make([]int, 3, 5) s3 := []{1, 2, 3} s4 := []string{99: ""} s5 := s1[1:3] # s5和s1共享同一個底層數組 s6 := s1[2:3:4] # s6是長度爲1,容量爲2的切片
切片會包含一個底層數組。
切片和數組的區別在於,[]中沒有數字。
對於底層數組容量是k的切片s[i:j],其長度是j-i,容量是k-i。
在對切片進行迭代時,返回的是切片元素的副本。每次迭代時,值副本的地址是相同的。
多維切片:
s := [][]int{{10}, {100, 200}}
切片包含地址指針、長度和容量。在64位計算機上,一個切片佔用24字節。複製切片時不會複製底層數組,所以將切片做爲參數傳遞給函數,開銷很小。
切片函數:
cap(s) # 容量 len(s) # 長度 append(s, element)
映射使用了散列表。在每次迭代中,各元素返回的次序可能不一樣。
創建映射:
m1 := make(map[int]int) m2 := map[string]string{"Red": "#da1337", "Orange": "#e95a22"}
映射的鍵必須是可使用==比較的對象。函數、切片、以及包含切片的對象,因爲具備引用語義,不能做爲映射的鍵。
從映射中獲取鍵對應的值時,若是鍵不存在,會返回零值。
映射函數:
delete(m, "key")
Go是靜態類型語言。
自定義類型字面值:
type user struct { name string email string } type admin struct { person user level string } u1 := user{"Lisa", "lisa@abc.com"} u2 := user{name: "Lisa", email: "lisa@abc.com"} a1 := admin{ person: user{"Lisa", "lisa@abc.com"} level: "super" }
以指針爲接收者的函數只能經過指針調用。以值爲接收者的函數能夠經過值或指針調用。對於以值爲接收者的函數,函數域中的接收者是值的副本,即便經過指針調用時也是如此。
package main import ( "log" ) func main() { u1 := user{"Tom"} u2 := &user{"Jerry"} u1.Name() u2.Name() log.Printf("%p %p", &u1, u2) } type user struct { name string } func (r user) Name() { log.Printf("%s %p %p", r.name, &r, &r.name) }
若是函數須要修改接收者的狀態,要以指針做爲接收者。不然使用值做爲接收者。
Go中的引用類型有:切片、映射、通道、接口和函數。
接口是用於定義行爲的類型。若是一個類型實現了某個接口所聲明的所有方法,這個類型的對象就能夠賦值給作對應接口類型的變量。在賦值完成後, 會創建一個接口對象。接口對象包含兩個指針:一個指向iTable,一個指向存儲的值。iTable包含了存儲值的類型信息,以及與這個值相關聯的一組方法,稱爲方法集。方法集定義了接口的接收規則。
值 | 方法接收者 |
T | (t T) |
*T | (t T) 和 (t *T) |
嵌入類型:
type user struct { name string email string } func (r user) hello() string { return "hello " + r.name } type admin struct { user level string } a := admin{} a.user.name a.name a.user.hello() a.hello()
被嵌入的類型也叫內部類型。內部類型中的標誌符(成員和函數)會被提高到外部類型中。
以小寫字母開頭的標識符是包私有標識符,在包外不可見。對於未公開的內部類型,其公開成員能夠經過標識符提高,之外部類型成員的方式訪問。
Go使用的同步模型是通訊順序模型(Communicating Sequential Processes,CSP),各個goroutine經過傳遞消息通訊,而非經過鎖和共享內存來共享狀態。
Go運行時會在邏輯處理器上調度goroutine。從1.5版本起,Go運行時會爲每一個物理處理器分配一個邏輯處理器(每一個CPU一個仍是每一個核一個?)。當goroutine指定到一個阻塞的系統調用時,運行時將線程和goroutine從邏輯處理器上分離。被分離的線程會繼續阻塞,等待系統調用返回。而邏輯處理器會創建一個新線程,從隊列中選取一個goroutine,將新線程和goroutine綁定到邏輯處理器上。
在處理網絡I/O時,goroutine會集成到網絡輪詢器的運行時。
Go運行時的線程數量默認爲10000。超過這個數量時,運行時會崩潰。使用runtime/debug包中的方法SetMaxThreads()能夠提升線程數量。
併發concurrency不是並行parallelism。並行是讓不一樣的代碼片斷同時在不一樣的物理處理器上執行。並行指同時處理不少事情。併發指同時管理不少事情。
調用runtime包的方法GOMAXPROCS()能夠設置Go運行時邏輯處理器數量。
next: for i := 0; i < 10; i++ { for j := 0; j < 10; j++ { if cond { continue next } } }
runtime.NumCPU()返回物理處理器數量。
runtime.Gosched()從線程退出,並放回隊列。
unbuffered := make(chan int) // 無緩衝區的通道 buffered := make(chan int, 10) // 有緩衝區的通道
無緩衝區通道要求發送方和接收方同時準備好。若是一方沒有準備好,另外一方會阻塞。
package main import ( "fmt" ) func main() { input := make(chan int) go func() { input <- 1 }() foo(input, 10) } func foo(input chan int, end int) { x := <-input fmt.Println(x) if x >= end { return } go foo(input, end) input <- x + 1 }
import ( "os" "os/signal" ) signalQueue := make(chan os.Signal) signal.Notify(signalQueue, os.Interrupt) # 接收信號 for { if interrupted() { break } // ... } func interrupted() bool { select { case <-signalQueue: signal.Stop(signalQueue) # 中止接收信號 return true default: return false } }
a := []int{1, 2} func add(arr ...int) { b := append(a, arr...) }
判斷超時和終端的示例:
interrupt := make(chan os.Signal, 1) complete := make(chan error) timeout := time.After(3 * time.Second) signal.Notify(r.interrupt, os.Interrupt) go func() { complete <- someFunc() }() select { case err := <-complete: return err case <-r.timeout: return "timeout" }
每一個調用signal.Notify(signalChan, signum)的隊列,都會收到信號。
import "log" log.SetPrefix("TRACE: ") log.SetFlags(log.Ldata | log.Llongfile) // Ldate Ltime Llongfile Lmicroseconds Lshortfile // LstdFlags = Ldate | Ltime log.Println("abc") log.Fatalln("abc") log.Panicln("abc") log.New(ioutil.Discard, "TRACE: ", log.LstdFlags|log.Lshortfile) log.New(io.MultiWriter(file, os.Stderr), "ERROR: ", log.LstdFlags|log.Lshortfile)
iota關鍵字爲每一個常量複製相同的表達式,而且每次自增1:
const ( a = 1 << iota // 1 b // 2 c // 4 ) const ( x = iota // 0 y // 1 z // 2 )
單元測試示例:
import "testing" func TestFoo(t *testing.T) { t.Log("abc") t.Logf("a = %v", 2) t.Errorf("%v", 123) t.Fatal("abc") }
測試web服務示例:
import ( "testing" "net/http" "net/http/httptest" ) feed := `<xml ...>` func MockServer() *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) w.Header().Set("Content-Type", "application/xml") fmt.Fprintln(w, feed) })) } func TestFoo(t *testing.T) { server := mockServer() defer server.Close() resp, err := http.Get(server.URL) if err != nil { t.Fatal(err) } defer resp.Body.Close() // ... }
測試web服務示例:
http.HandleFunc("/some", func(rw http.ResponseWriter, r *http.Request) { // ... }) func TestSome(t *testing.T) { req, _ := http.NewRequest("GET", "/some", nil) rw := httptest.NewRecorder() http.DefaultServeMux.ServeHTTP(rw, req) }
基準測試:
func BenchmarkSprintf(b *testing.B) { number := 10 b.ResetTimer() for i := 0; i < b.N; i++ { fmt.Sprintf("%d", number) } }