go study
go语言有一个获取远程包的工具就是go get
,目前go get支持多数开源社区(例如:GitHub、googlecode、bitbucket、Launchpad)
这个命令在内部实际上分成了两步操作:第一步是下载源码包,第二步是执行go install
go get github.com/astaxie/beedb
go get -u 参数可以自动更新包,而且当go get的时候会自动获取该包依赖的其他第三方包
go test
执行这个命令,会自动读取源码目录下面名为*_test.go
的文件,生成并运行测试用的可执行文件。
-v
显示测试的详细命令
godoc
godoc -http=:8080
浏览器中打开127.0.0.1:8080
,会看到一个golang.org的本地copy版本,可以查询pkg文档等其它内容
详解
package <pkgName>
(在我们的例子中是package main
)这一行告诉我们当前文件属于哪个包,而包名main
则告诉我们它是一个可独立运行的包,它在编译后会产生可执行文件。除了main
包之外,其它的包最后都会生成*.a
文件(也就是包文件)并放置在$GOPATH/pkg/$GOOS_$GOARCH
中(以Mac为例就是$GOPATH/pkg/darwin_amd64
)。
每一个可独立运行的Go程序,必定包含一个
package main
,在这个main
包中必定包含一个入口函数main
,而这个函数既没有参数,也没有返回值。
函数是通过<pkgName>.<funcName>
的方式调用的
前面提到过,包名和包所在的文件夹名可以是不同的,此处的<pkgName>
即为通过package <pkgName>
声明的包名,而非文件夹名。
使用var
关键字是Go最基本的定义变量方式,与C语言不同的是Go把变量类型放在变量名后面:
_
(下划线)是个特殊的变量名,任何赋予它的值都会被丢弃。
Go对于已声明但未使用的变量会在编译阶段报错
在Go中,布尔值的类型为bool
,值是true
或false
,默认为false
。
错误类型
Go内置有一个error
类型,专门用来处理错误信息,Go的package
里面还专门有一个包errors
来处理错误:
err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
fmt.Print(err)
}
Go程序设计的一些规则
- 大写字母开头的变量是可导出的,也就是其它包可以读取的,是公有变量;小写字母开头的就是不可导出的,是私有变量。
- 大写字母开头的函数也是一样,相当于
class
中的带public
关键词的公有函数;小写字母开头的就是有private
关键词的私有函数。
array
array
就是数组,它的定义方式如下:
var arr [n]type
slice
slice
并不是真正意义上的动态数组,而是一个引用类型。slice
总是指向一个底层array
,slice
的声明也可以像array
一样,只是不需要长度。
// 和声明array一样,只是少了长度
var fslice []int
注意slice
和数组在声明时的区别:声明数组时,方括号内写明了数组的长度或使用...
自动计算长度,而声明slice
时,方括号内没有任何字符。
slice
是引用类型,所以当引用改变其中元素的值时,其它的所有引用都会改变该值
对于slice
有几个有用的内置函数:
len
获取slice
的长度cap
获取slice
的最大容量append
向slice
里面追加一个或者多个元素,然后返回一个和slice
一样类型的slice
copy
函数copy
从源slice
的src
中复制元素到目标dst
,并且返回复制的元素的个数
map
map`也就是Python中字典的概念,它的格式为`map[keyType]valueType
// 声明一个key是字符串,值为int的字典,这种方式的声明需要在使用之前使用make初始化
var numbers map[string]int
// 另一种map的声明方式
numbers = make(map[string]int)
使用map过程中需要注意的几点:
map
是无序的,每次打印出来的map
都会不一样,它不能通过index
获取,而必须通过key
获取map
的长度是不固定的,也就是和slice
一样,也是一种引用类型- 内置的
len
函数同样适用于map
,返回map
拥有的key
的数量 map
的值可以很方便的修改,通过numbers["one"]=11
可以很容易的把key为one
的字典值改为11
map
和其他基本型别不同,它不是thread-safe,在多个go-routine存取时,必须使用mutex lock机制
make、new操作
make
用于内建类型(map
、slice
和channel
)的内存分配。new
用于各种类型的内存分配。
new
返回指针。
零值
关于“零值”,所指并非是空值,而是一种“变量未填充前”的默认值,通常为0。 此处罗列 部分类型 的 “零值”
int 0
int8 0
int32 0
int64 0
uint 0x0
rune 0 //rune的实际类型是 int32
byte 0x0 // byte的实际类型是 uint8
float32 0 //长度为 4 byte
float64 0 //长度为 8 byte
bool false
string ""
goto
Go有goto
语句——请明智地使用它。用goto
跳转到必须在当前函数内定义的标签。
for
Go里面最强大的一个控制逻辑就是for
,它既可以用来循环读取数据,又可以当作while
来控制逻辑,还能迭代操作。
for a; b; c {
//...
}
a、b和c都是表达式,其中a和c是变量声明或者函数调用返回值之类的,b是用来条件判断,a在循环开始之前调用,c在每轮循环结束之时调用。
在循环里面有两个关键操作break
和continue
,break
操作是跳出当前循环,continue
是跳过本次循环。当嵌套过深的时候,break
可以配合标签使用,即跳转至标签所指定的位置。
break
和continue
还可以跟着标号,用来跳到多重循环中的外层循环
for
配合range
可以用于读取slice
和map
的数据
由于 Go 支持 “多值返回”, 而对于“声明而未被调用”的变量, 编译器会报错, 在这种情况下, 可以使用_
来丢弃不需要的返回值
函数
函数是Go里面的核心设计,它通过关键字func
来声明,它的格式如下:
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
//这里是处理逻辑代码
//返回多个值
return value1, value2
}
上面的代码我们看出
- 关键字
func
用来声明一个函数funcName
- 函数可以有一个或者多个参数,每个参数后面带有类型,通过
,
分隔 - 函数可以返回多个值
- 上面返回值声明了两个变量
output1
和output2
,如果你不想声明也可以,直接就两个类型 - 如果只有一个返回值且不声明返回值变量,那么你可以省略 包括返回值 的括号
- 如果没有返回值,那么就直接省略最后的返回信息
- 如果有返回值, 那么必须在函数的外层添加return语句
我们知道,变量在内存中是存放于一定地址上的,修改变量实际是修改变量地址处的内存。只有add1
函数知道x
变量所在的地址,才能修改x
变量的值。所以我们需要将x
所在地址&x
传入函数,并将函数的参数的类型由int
改为*int
,即改为指针类型,才能在函数中修改x
变量的值。此时参数仍然是按copy传递的,只是copy的是一个指针。
这样,我们就达到了修改x
的目的。那么到底传指针有什么好处呢?
- 传指针使得多个函数能操作同一个对象。
- 传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当你要传递大的结构体的时候,用指针是一个明智的选择。
- Go语言中
channel
,slice
,map
这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变slice
的长度,则仍需要取地址传递指针)
defer
Go语言中有种不错的设计,即延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。
Panic和Recover
Go没有像Java那样的异常机制,它不能抛出异常,而是使用了panic
和recover
机制。一定要记住,你应当把它作为最后的手段来使用,也就是说,你的代码中应当没有,或者很少有panic
的东西。这是个强大的工具,请明智地使用它。那么,我们应该如何使用它呢?
Panic
是一个内建函数,可以中断原有的控制流程,进入一个
panic
状态中。当函数F
调用panic
,函数F的执行被中断,但是F
中的延迟函数会正常执行,然后F返回到调用它的地方。在调用的地方,F
的行为就像调用了panic
。这一过程继续向上,直到发生panic
的goroutine
中所有调用的函数返回,此时程序退出。panic
可以直接调用panic
产生。也可以由运行时错误产生,例如访问越界的数组。
Recover
是一个内建的函数,可以让进入
panic
状态的goroutine
恢复过来。recover
仅在延迟函数中有效。在正常的执行过程中,调用recover
会返回nil
,并且没有其它任何效果。如果当前的goroutine
陷入panic
状态,调用recover
可以捕获到panic
的输入值,并且恢复正常的执行。
main
函数和init
函数
Go里面有两个保留的函数:init
函数(能够应用于所有的package
)和main
函数(只能应用于package main
)。这两个函数在定义时不能有任何的参数和返回值。虽然一个package
里面可以写任意多个init
函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个package
中每个文件只写一个init
函数。
1.点操作
import(
. "fmt"
)
这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名
2.别名操作
import(
f "fmt"
)
别名操作顾名思义我们可以把包命名成另一个我们用起来容易记忆的名字
3._操作
import (
"database/sql"
_ "github.com/ziutek/mymysql/godrv"
)
_操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数。