顺序编程

变量

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
close(ch)

同步锁

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
$ go doc foo

代码格式化

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

单元测试

1
$ go test calc

模块管理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