@delight
2016-01-02T15:57:51.000000Z
字数 4569
阅读 1773
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 int
select{
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
有很多方法进行值的转换;