顺序编程 变量 1 2 3 4 5 6 7 8 9 10 var v10 int v10 = 123 v11 := 234 // 匿名变量示例 func GetName() (firstName, lastName nickName string) { return "May", "Chan", "Chibi" } _, _, nickName := GetName()
常量 iota是一个特殊的常量,它会在每一个const关键字出现时重置为0,然后在下一个const出现前,每出现一次iota,它代表的值加1。如果两个const的赋值语句表达式一样,后一个赋值可省略。
1 2 3 4 5 const ( c0 = iota c1 = iota c2 )
枚举 1 2 3 4 5 6 7 8 9 10 const ( Sunday = iota Monday Tuesday Wednesday Thursday Friday Saturday numberOfDays )
类型
布尔:bool
整型:int8、byte、int16、int、uint、unitptr等
浮点:float32、float64
复数:complex64、complex128
字符串:string:在初始化后不能被修改
字符:rune
错误:error
指针(pointer)
数组(array)
切片(slice):可动态增减元素
字典(map)
通道(chan)
接口(interface)
结构体(struct)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // 切片例子 mySlice := make([]int, 5, 10) fmt.Println("len(mySlice):", len(mySlice)) fmt.Println("cap(mySlice):", cap(mySlice)) mySlice = append(mySlice, 1, 2, 3) mySlice2 := []int{8, 9, 10} 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // Map例子 package main import ( "fmt" ) type PersonInfo struct{ ID string Name string Address string } func main(){ var personDB map[string] PersonInfo personDB = make(map[string] PersonInfo) personDB["12345"] = PersonInfo{"12345", "Tom", "Room 12345"} personDB["546"] = PersonInfo{"546", "Tom", "Room 546"} person, ok := personDB["1234"] if ok { fmt.Println("Found person", person.Name) }else{ fmt.Println("Did not find person") } delete(personDB, "546") fmt.Println(personDB["12345"].Name) }
流程控制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 // if else func if_else(x int) int { if x == 0 { return 5 }else{ return x } } // switch case func switch_case(i int){ //无需break switch i{ case 0: fmt.Println("0") case 1: fmt.Println("1") default: fmt.Println("Default") } } // for sum := 0 for { sum ++ if sum > 10 { break } } for j := 0; j < 5; j++ { ftm.Println(j) } // goto func myfunc(){ i := 0 HERE: fmt.Println(i) i++ if i < 10 { goto HERE } }
函数 1 2 import "mymath" //假设Add被放在一个叫mymath的包中 c := mymath.Add(1, 2)
小写字母开头的函数只在本包内可见,大写字母开头的函数才能被其他包使用。
1 2 3 4 5 6 7 8 9 10 11 12 func myfunc(args ...int) { for _, arg := range args { fmt.Println(arg) } } func myfunc(args ...int) { for _, arg := range args { fmt.Println(arg) } myfunc2(args...) }
任意类型的不定参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func Printf(format string, args ...interface{}){ for _, arg := range args { switch arg.(type) { case int: fmt.Println(arg, "is an int value") case string: fmt.Println(arg, "is a string value") case int64: fmt.Println(arg, "is an int64 value") default: fmt.Println(arg, "is an unknown type") } } }
多返回值
1 func (file *File) Read(b []byte) (n int, err Error)
匿名函数与闭包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package main import( "fmt" ) func main(){ var j int = 5 a := func()(func()){ var i int = 10 return func() { fmt.Printf("i, j: %d, %d\n", i, j) } }() a() j *= 2 a() } // 结果 // i, j: 10, 5 // i, j: 10, 10
异常处理 1 2 3 type error interface { Error() string }
大多数函数,如果要返回错误,可以定义如下模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func Foo(param int)(n int, err error) { // ... } n, err := Foo(0) if err != nil { // 错误处理 } else { // 使用返回值n } type PathError struct { Op string Path string Err error } func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() }
defer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func CopyFile(dst, src string) (w int64, err error){ srcFile, err := os.Open(src) if err != nil { return } defer srcFile.Close() dstFile, err := os.Create(dst) if err != nil { return } defer dstFile.Close() return io.Copy(dstFile, srcFile) }
panic/recover
1 2 3 4 5 defer func() { if r := recover(); r != nil { log.Printf("Runtime error caught: %v", r) } }()
面向对象编程 结构体 1 2 3 4 type Integer int func (a Integer) Less(b Integer) bool { return a < b }
1 2 3 4 5 6 7 type Header map[string] []string func (h Header) Add(key, value string){ textproto.MIMEHeader(h).Add(key, value) } func (h Header) Set(key, value string){ textproto.MIMEHeader(h).Set(key, value) }
引用类型:数组切片、map、channel、接口(interface) 值类型:byte、int、bool、float32、float64、string、array、struct、pointer等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 type Animal struct { Name string //名称 Color string //颜色 Height float32 //身高 Weight float32 //体重 Age int //年龄 } //奔跑 func (a Animal)Run() { fmt.Println(a.Name + "is running") } type Lion struct { Animal //匿名字段 } func main(){ var lion = Lion{ Animal{ Name: "小狮子", Color: "灰色", }, } lion.Run() fmt.Println(lion.Name) }
匿名组合相当于以其类型名称(去掉包名部分)作为成员变量的名字。 Go语言中,要使某个符号对其他包可见,需要将该符号定义为以大写字母开头。Go语言中符号的可访问性是包一级别,而不是类型级别。
接口 Go语言中,一个类只需要实现了接口要求的所有函数,就说这个类实现了该接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 type File struct { // ... } func (f *File) Read(buf []byte) (n int, err error) func (f *File) Write(buf []byte) (n int, err error) func (f *File) Close() error type IFile interface{ IReader IWrite IClose } type IReader interface{ Read(buf []byte) (n int, err error) } type IWriter interface{ Write(buf []byte) (n int, err error) } type IClose interface{ Close() error } var file1 IFile = new(File) var file2 IReader = new(File)
Go语言中任何对象实例都满足空接口interface{}。
并发编程 go channel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package main import "fmt" func Count(ch chan int){ fmt.Println("Counting") ch <- 1 } func main(){ chs := make([]chan int, 10) for i :=0; i < 10; i++ { chs[i] = make(chan int) go Count(chs[i]) } for _, ch := range(chs) { a := <- ch fmt.Println(a) } }
超时机制
1 2 3 4 5 6 7 8 9 10 11 12 timeout := make(chan bool, 1) go func(){ time.Sleep(1e9) timeout <- true }() select { case <-ch: //从ch中读取到数据 case <-timeout //一直没有从ch中读取到数据,但从timeout中读取到了数据 }
channel的传递
1 2 3 4 5 6 7 8 9 10 11 12 type PipeData struct { value int handler func(int) int next chan int } func handle(queue chan *PipeData){ for data := range queue{ data.next <- data.handler(data.value) } }
关闭channel
同步锁
1 2 3 4 5 6 var l sync.Mutex func foo(){ l.Lock() defer l.Unlock() //... }
全局唯一操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var a string var once sync.once func setup(){ a = "hello" } func doprint() { once.Do(setup) print(a) } func twoprint(){ go doprint() go doprint() }
以上代码setup只会运行一次。
网络编程 Socket编程 Dial():连接,支持如下协议:tcp,tcp4,tcp6,udp,udp4,udp6,ip,ip4,ip6
1 2 3 conn, err := net.Dial("tcp", "192.168.0.2:2100") conn2, err := net.Dial("udp", "192.168.0.2:53") conn3, err := net.Dial("ip4:icmp", "www.baidu.com")
Write()与Read()方法来发送数据与接收数据。
HTTP编程 net/http包,包含HTTP客户端与服务端的具体实现。
1 2 3 4 5 6 resp, err := http.Get("http://example.com/") if err != nil{ return } defer resp.Body.close() io.Copy(os.Stdout, resp.Body)
1 2 3 4 5 6 7 resp, err := http.Post("http://example.com/upload", "image/jpeg", &imageDataBuf) if err != nil{ return } if resp.StatusCode != http.StatusOK{ return }
1 2 3 4 resp, err := http.PostForm("http://example.com/posts", url.Values{"title": {"article title"}, "content": {"artical body"}}) if err != nil{ return }
1 resp, err := http.Head("http://example.com/")
1 2 3 4 5 req, err := http.NewRequest("GET", "http://example.com/posts", nil) req.Header.Add("User-Agent", "Gobook User-Agent") client := &http.Client{...} resp, err := client.Do(req)
1 2 3 http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request){ fmt.Fprintf(w, "hello") })
RPC net/rpc,包实现RPC协议的细节
1 func (t *T) MethodName(argType T1, replyType *T2) error
T1与T2默认会使用encoding/gob包进行编码与解码。 argType表示由RPC客户端传入的参数 replyType表示要返回给RPC客户端的结果 服务端示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package main import ( "log" "net" "net/http" "net/rpc" ) type Args struct{ A, B int } type Arith int func (t *Arith) Multiply(args *Args, reply *int) error{ *reply = args.A * args.B return nil } func main(){ arith := new(Arith) rpc.Register(arith) rpc.HandleHTTP() l, e := net.Listen("tcp", ":1234") if e != nil{ log.Fatal("listen error", e) } http.Serve(l, nil) }
客户端示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package main import ( "net/rpc" "fmt" "log" ) type Args struct{ A, B int } func main(){ // 顺序执行RPC client, _ := rpc.DialHTTP("tcp", "127.0.0.1:1234") args := &Args{7,8} var reply int err := client.Call("Arith.Multiply", args, &reply) if err != nil{ log.Fatal(err) } fmt.Println(reply) //异步执行RPC var reply2 int divCall := client.Go("Arith.Multiply", &Args{6,8}, &reply2, nil) divCall = <-divCall.Done fmt.Println(reply2) }
JSON encoding/json标准库 json.Marshal(),将一组数据进行JSON格式化编码 json.Unmarshal(),将JSON格式的文档解码为Go里边预期的数据结构。
1 2 3 4 5 6 7 8 data := make(map[string] string) data["name"] = "Michael" data2, err := json.Marshal(data) if err != nil{ fmt.Println("err") return } fmt.Println(string(data2[:]))
1 2 3 4 5 data := make(map[string] string) data_json := []byte("{\"name\":\"Michael\"}") json.Unmarshal(data_json, &data) fmt.Println(data)
反射 1 2 3 4 5 6 7 8 9 10 11 12 type T struct{ A int B string } t := T{203, "mh203"} s := reflect.ValueOf(&t).Elem() typeOfT := s.Type() for i := 0; i < s.NumField(); i++ { f := s.Field(i) fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface()) }
Go工具 包文档查看(网站形式)
1 2 $ godoc -http=:8080 $ godoc -http=:8080 -path="."
包文档查看(命令行形式)
代码格式化
1 2 $ go fmt hello.go $ gofmt -l -w .
项目构建 go build:用于测试编译包,在项目目录下生成可执行文件(有main包);不能生成包文件 go install:主要用来生成库和工具。一是编译包文件(无main包),将编译后的包文件放到 pkg 目录下($GOPATH/pkg)。二是编译生成可执行文件(有main包),将可执行文件放到 bin 目录($GOPATH/bin);生成可执行文件在当前目录下, go install 生成可执行文件在bin目录下($GOPATH/bin)
1 2 $ GOOS=linux GOARCH=amd64 go build calc $ go install calc
GOOS:系统,linux/darwin/windows GOARCH:386/amd64
单元测试
模块管理go mod
1 2 3 $ go mod download # 下载指定模块 $ go mod init test.com/go #初始化当前模块 $ go mod vendor # 将依赖复制到当前vendor目录下
使用go module后,项目代码不需要放在GOPATH目录下。import项目的package时使用module路径。
以下是beego应用使用module来管理依赖的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $ cat <<EOF > go.mod module beegoapp go 1.12 replace ( golang.org/x/crypto => github.com/golang/crypto v0.0.0-20191227163750-53104e6ec876 golang.org/x/mod => github.com/golang/mod v0.1.0 golang.org/x/net => github.com/golang/net v0.0.0-20191209160850-c0dbc17a3553 golang.org/x/sync => github.com/golang/sync v0.0.0-20190911185100-cd5d95a43a6e golang.org/x/sys => github.com/golang/sys v0.0.0-20191228213918-04cbcbbfeed8 golang.org/x/text => github.com/golang/text v0.3.2 golang.org/x/tools => github.com/golang/tools v0.0.0-20191230220329-2aa90c603ae3 golang.org/x/xerrors => github.com/golang/xerrors v0.0.0-20191204190536-9bdfabe68543 ) require ( github.com/astaxie/beego v1.12.0 github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect github.com/smartystreets/goconvey v1.6.4 ) EOF