通道(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) } }