Go系列一
一、语法基础
1、package:声明包。类似Java。Go程序是由包组成的。程序入口的包是main。
2、import:导入包。类似Java。利用圆括号可以导入多个包,或者逐个导入。
3、func:定义函数。
4、导出的名称,首字母是大写。
5、变量类型在变量名后面。
6、函数的命名参数,多个连续同类型,可以只保留最后一个类型。
x int, y int // 简写成: x, y int
7、函数返回值位于函数签名的尾部。
8、函数返回值可以有名字,并且像变量一样使用。
9、return语句没有参数,即返回各个变量的当前值。
10、:=,短声明变量,明确类型的赋值语句,只能用在函数内。
11、基本类型:bool,string,int,int8,int16,int32,int64,uint8,uint16,uint32,uint64,uintptr,byte,rune,float32,float64,complex64,complex128。
12、var可以利用圆括号包含多个变量声明。
13、没有初始化的变量,会被赋值为零值。类似C/C++中的默认值。数值类型为0,布尔类型为false,字符串类型为"",指针类型为nil,slice为nil,map为nil。
14、不同类型之间的转换,需要显式指定。C/C++支持隐式类型转换。
15、类型推导:如果右值有明确类型,而左值没有,则左值的类型由右值推导得出。
16、常量(const)不能使用:=。
17、只有for循环,并且没有圆括号,还可以在没表达式时省略分号。没有while循环。
// 死循环 for { }
18、if条件语句没有圆括号。条件表达式前面可以有语句,并且语句中的变量可以在if和else中使用。
19、switch条件语句没有圆括号。条件表达式前面可以有语句。没有条件表达式,则等价于true。从上到下执行,直到匹配成功。
20、defer:延迟函数调用。类似Objective-C的dispatch_async。defer函数会被压栈。
21、指针不能运算。跟C/C++不同。
var p * int
22、结构体,利用点号访问字段。结构体指针也是用点号访问字段。
type DefaultCreator struct { clientSet *kubernetes.Clientset }
23、结构体文法,通过字段值列表来创建一个结构体,键值对可以只列出部分字段。
type Vertex struct { X, Y int } var ( v1 = Vertex{1, 2} // 类型为 Vertex v2 = Vertex{X: 1} // Y:0 被省略 v3 = Vertex{} // X:0 和 Y:0 p = &Vertex{1, 2} // 类型为 *Vertex )
24、数组,长度是类型的一部分,不能改变。
var a [2]string a[0] = "Hello" a[1] = "World" fmt.Println(a[0], a[1]) fmt.Println(a)
25、slice(切片),用make函数创建,用append函数添加元素。
// 序列 p := []int{2, 3, 5, 7, 11, 13} len(p) p[low:high] // 从下标low到high-1,省略low表示0,省略high表示len(p)
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
// 如果s太小,会分配更大的数组 func append(s []T, vs ...T) []T
26、for循环可以用range遍历slice和map。类似Python。
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} for i, v := range pow { }
27、range遍历可以使用下划线忽略key(slice的下标)和value。
for _, val := range pow { }
28、map,用make函数创建。
type Vertex struct { Lat, Long float64 } var m map[string]Vertex // 不能添加元素 m = make(map[string]Vertex) // 用make才能添加元素 m["Bell Labs"] = Vertex{ 40.68433, -74.39967, }
29、map文法。类似结构体文法,不过有键。
type Vertex struct { Lat, Long float64 } var m = map[string]Vertex{ "Bell Labs": Vertex{ 40.68433, -74.39967, }, "Google": Vertex{ 37.42202, -122.08408, }, }
var m = map[string]Vertex{ "Bell Labs": {40.68433, -74.39967}, "Google": {37.42202, -122.08408}, }
delete(m, "Google") // 删除键值对 v, ok := m["Answer"] // 检查是否包含键值对
30、函数也是值。类似C/C++函数指针。
hypot := func(x, y float64) float64 { return math.Sqrt(x*x + y*y) } hypot(3, 4)
31、闭包
func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } pos, neg := adder(), adder() for i := 0; i < 10; i++ { pos(i) neg(-2*i) }
32、方法:没有类。包内任意数据类型都可以定义任意方法。但是其他包和基础数据类型不行。
type Vertex struct { X, Y float64 } // Abs方法接收器是Vertex结构体的指针,即方法属于哪个数据类型 func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } v := &Vertex{3, 4} // 方法接收器通过点号访问方法 v.Abs()
33、方法接收器是指针:1、可以避免方法调用时值拷贝;2、可以修改接收器。
34、接口类型是一组方法的集合。接口类型的值可以是实现接口方法的任何类型。
type Abser interface { Abs() float64 } type Vertex struct { X, Y float64 } func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } var a Abser v := Vertex{3, 4} a = &v
35、隐式接口。
36、fmt包中的Stringer接口,error接口。io包中的Reader接口。image包。
type Stringer interface { String() string }
// nil表示成功 type error interface { Error() string }
37、http包,实现Handler接口来响应http请求。
package http type Handler interface { ServeHTTP(w ResponseWriter, r *Request) }
38、goroutine,轻量级线程。其实就是协程。共享进程的地址空间。
go f(x, y, z) // 创建一个新的goroutine来运行f函数。
39、channel,有类型的管道。类似C/C++中的pipe。
func sum(a []int, c chan int) { sum := 0 for _, v := range a { sum += v } c <- sum // 将和送入 c } a := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(a[:len(a)/2], c) go sum(a[len(a)/2:], c) x, y := <-c, <-c // 从 c 中获取
// 缓冲channel,100是缓冲的长度,缓冲满时,发送阻塞;缓冲空时,接收阻塞。 ch := make(chan int, 100)
40、for i := range ch循环会不断从channel接收值,直到channel被发送方关闭。
v, ok := <-ch // 检查channel是否关闭
41、select,可以让一个goroutine在多个通信操作上阻塞等待,默认是default分支,语法很像switch。其实就是Linux内核中select的功能。
func fibonacci(c, quit chan int) { x, y := 0, 1 for { select { case c <- x: x, y = y, x+y case <-quit: fmt.Println("quit") return } } } c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit)
42、指针也是利用点号来访问。
二、项目开发
1、使用go命令行工具,需要按照固定目录结构来组织代码。因为它是为了维护公共代码库(例如github)中的开源代码而设计的。
2、GOPATH环境变量,指定工作目录。不能跟Go的安装目录相同。工作目录的bin子目录要添加到PATH环境变量中。
export PATH=$PATH:$GOPATH/bin
3、工作目录包含3个子目录:bin(可执行命令),pkg(包对象),src(源文件,按包组织,每个目录一个包)。
bin/ streak # 可执行命令 todo # 可执行命令 pkg/ linux_amd64/ code.google.com/p/goauth2/ oauth.a # 包对象 github.com/nf/todo/ task.a # 包对象 src/ code.google.com/p/goauth2/ .hg/ # mercurial 代码库元数据 oauth/ oauth.go # 包源码 oauth_test.go # 测试源码 github.com/nf/ streak/ .git/ # git 代码库元数据 oauth.go # 命令源码 streak.go # 命令源码 todo/ .git/ # git 代码库元数据 task/ task.go # 包源码 todo.go # 命令源码
4、包路径,可以是任意的唯一路径。
// 比较好的规范。基本路径 $GOPATH/src/github.com/yangwenhuan
5、构建并安装。构建好的二进制,默认安装在bin目录下。
// helloworld目录下有helloworld.go。 // 可以在任意路径执行这个命令,go命令行工具会根据GOPATH 到github.com/yangwenhuan/helloworld路径下查找源文件。 // 如果在helloworld目录下,直接执行go install即可。 go install github.com/yangwenhuan/helloworld