@delight
2016-01-02T07:57:51.000000Z
字数 4569
阅读 1969
go
其实前段时间我已经试着学习Go了,但是由于没啥项目需求所以很快就忘了,这次借着要写heka插件的机会重新学习一下,巩固一下基础知识。
GOPATH,这个是用默认包的存放位置,用go get安装的包会存放在这个位置。在~/.zshrc或~/.bashrc里面加入export GOPATH=~/.go,然后在PATH里面加入GOPATH/bin即可;go get命令下载必定被墙,使用git config --global http.proxy "xxxx:oooo"设置代理方可使用,也可以使用http_proxy=xxxx:oooo go get这个格式,或者在bashrc里面加个alias;gore(go get -u github.com/motemen/gore),本地调试使用;package xxx,类似java,import可以用括号打包;var name int是典型的声明变量格式,自动推导类型的语法是name := 0(但是这个语法只能在函数体里面用,外面必须用var声明)。可以在一行给多个变量赋值(类似python的解包);bool, int, uint, byte(uint8),rune(int32), float32, float64, uintptr, complex64, complex128, string,注意么有double,类似其他GC语言,所有类型会被自动化为零值;int和string之间不能互转,可以用strconv中的Itoa和Atoi来完成;const关键字声明,常量只能是基础类型,且不能用:=声明。常量的实际类型由上下文决定,数值常量本身是高精度的;for语句,且不需要括号(后面的都不需要),基本格式还是类似c的for i := 0; i < 10; ++i,这种,后面必须跟大括号,且大括号必须和for在同一行…for就成了while;如果全部省略,裸的for代表死循环;else里面;switch语句,好吧,和上面也类似。有个有趣的地方是,默认自动终止,除非使用fallthrough,和C中的默认自动向下,除非手动break相反;switch也可以直接用空语句,条件比较复杂时使用可以让代码看起来更加整洁;defer语句,这是Go的特色语句了…defer是在函数返回后再执行,其本质时压栈,所以弹出顺序与defer的顺序相反;*T表示指向类型T的指针,取地址仍然使用&。不过与C不一样的是,不允许指针运算;struct,而且蛋疼的是,也只能拥有字段(和C一样)。结构体通过指针访问字段也是使用.符号(没有了->符号);{}进行结构体初始化,如
type Point struct {X, Y float32}var (a = Point{X: 10}b = Point{1, 1}c = Point{}p = &Point{1, 2})fmt.Println(p.X)
虽然感觉有点奇怪,不过和C++11后的初始化列表其实挺像的。
var a [10]int,这语法也是醉了。和C一样,数组不能动态扩张;slice代替数组,声明方式: a = make([]int, 0, 5),第二个参数表示长度(len),第三个参数表示容量(cap)。类似python中的list,可以切片;注意,如果声明var []a那么a==nil是成立的;append往slice中添加元素,类似C++中的vector可以自动扩展长度;range关键字(注意这货不是函数。。)用来对slice进行循环,格式是for i, v := range a;map现在也是新兴语言的标配了,map和slice一样,必须通过make创建,语法是m := make(map[string]int),[]中的是键的类型,后面跟着的是值的类型。初始化语法神马的和struct类似;delete关键字;检测存在使用双赋值:a, ok = m['test'],如果存在则ok为true,否则为false;static局部变量);Point)
func (p1 *Point) distance(p2 *Point) int{//...}
方法接受者位置在func关键字和函数名之间,呃,其实和C++的外置方法声明还是有点像的…
2. 值的注意的是,不仅仅是struct,可以通过这种声明向本包内任意非内置类型注入方法,甚至可以通过type声明别称后向别称的内置类型进行注入;
3. 方法接受者可以是指针,也可以不是,当然只有指针才能改变元素的实际值;
struct从语法上来讲和C基本是一样的;tag,在反射的时候用;interface声明一种接口:
type Flyable struct{Fly();}
上面Flyable声明了一个接口,拥有Fly方法. 这样后面假设我给pig加上fly方法,那么变量var item Flyable就可以被赋值为item = &pig{}
这里值得注意的是,这里的接口实现本质是隐式的,或者可以说是duckable的,pythoner对此应该深有理解:)
2. Stringers是一个常见的接口,类似python中的__str__或者java中的toString,它只需要实现String方法;
3. Go里面没有异常,仍然使用错误。error是一个接口,只有一个方法Error() string,通常函数会返回一个error,放在第二个位置,如果其不为nil则说明出了错误;
4. 其他常见接口包括io.Reader,表示从数据流结尾读取;http.Handler表示处理HTTP请求的服务器;image.Image表明一个图像的接口;
goroutine是Go运行时的轻量级线程,在方法名前加go就在另一个线程中同步执行了;channel是有类型的管道,可以使用<-操作符对其发送或接受值,使用make(chan int, 100)创建一个int的channel,第二个参数表示缓冲区长度,也可以不带,表示完全无缓冲;close一个channel表示不再发送数据(只有发送者可以关闭),向已经close的channel发送数据会引起panic。使用range则表示从channel中源源不断的接受数据直到被关闭;select语句使得一个goroutine在多个通讯操作上等待,阻塞直到某个分支可行,例如:
// var a, b chan intselect{case x <- a://...case <- b://...default://...}
当所有分支都不可行时,执行default语句;
5. sync.Mutex提供了互斥锁,包括Lock和Unlock两个方法,可以使用defer语句保证锁一定会被释放;
至此,基础部分结束。
GOPATH环境变量,这个路径就是实际的工作空间。从结论来看,Go提倡将所有Go语言项目放入同一个工作路径,当然也可以使用虚拟环境,类似python。go get命令,那么GOPATH下会自动创建bin, pkg和src三个文件夹,源码存放在src之下,import本地包时,就是从这一层开始的;go install会生成输出文件(可执行或者库),go build则仅编译;go fmt用来对代码进行格式化;godoc生成文档,类似python的docstring,但是约定更加简单:对类型、变量、常量、函数或者包的注释,在其定义前编写普通的注释即可,不要插入空行。Godoc 将会把这些注释识别为对其后的内容的文档。getter没有必要用Get开头,直接大写首字母就行,setter还可以留着Set;new用来分配内存,并且填0,返回指向对象的指针,程序可以利用这些指针进行手动初始化;make则只能用来创建内置类型(slice, map和channel),返回的是对象本身,而不是指针;array是一种对象,和它的大小相关;array名并不是指针(和C不同);print语系和C中基本一致;interface {}相当于C中的void *可以被转化为任意类型,一种常见的反射方式是使用v.(type),比如str, ok = v.(string),返回的就是string类型;另外可以在switch语句里面用x.(type),然后再case里面判断类型;import后必须使用,否则会报错(傻逼设定。。),可以用import _ "fmt"的方法导入但不使用,或者用_赋值;struct里面塞匿名字段(另一个struct)来达到继承的目的,虽然看起来很奇怪就是了;同样,也可以往interface里面塞一个别的interface达到继承接口的目的;panic和recover是最后手段;reflect包完成。reflect.TypeOf和reflect.ValueOf分别获得接口的实际类型和值;一般把接口类型称作Type,实际类型称为Kind。可以使用x.interface().(int)类似的语法将reflect.Value恢复成原来的值,使用printf时,可以直接将空接口里面的类型信息反射出来;settable的,换句话说,我们应该试图取得对象的指针,而不是对象本身。通过Elem方法拿到指针指向的对象,然后再进行修改;Type和Value有很多方法进行值的转换;