执行顺序
import --> const --> var --> init()
image.png
开发速度快
- 编译快,因为只关注被直接引入的库。
- 强类型,省略了许多心力检查类型错误。
并行而非并发
大多数语言线程是并发,即在某一时间段来看,是多个任务一起执行,但在某一时间点,其实只执行了一个。
并行是在同一时间点一同执行的多个任务(利用了多核)。
数据类型注意
- 字符串可以用数组下标方式取值,但不能赋值,会报编译型错误。
- 数组的赋值是 值 传递,作为参数是一次复制操作。
- 数组与切片的区别,指定元素个数的是数组,没指定的是切片
- 切片取值是取的下标前闭后开的(myArray[2, 4])。
- 切片的赋值是引用传递,若要值传递,用
copy(dst, src)
“nil != nil” 的问题
- 同为 nil 的两个不同类型的 error 类型,它们不相等,
type Aerr struct{}
func (*Aerr) Error() string {
return "got an A error"
}
func getAErr() *Aerr {
return nil
}
func getCommongErr() error {
return getAErr()
}
func main() {
err := getCommongErr()
println(err == nil) // false
return
}
其他细节
- defer 是栈式的调用原则。
-
goroutine
可以充分利用多核 cpu(runtime.GOMAXPROCS(n)
可以设置),是协程,类似线程,但比线程更轻量,占资源更少。像 java 也用了多核,但开启和关闭一个线程开销很大。 - go 使用了组合设计模式,只需简单将一个类型嵌入到另一个类型,就能复用所有功能。
- go 使用了行为建模,意思是实现了这些行为(方法),就是继承了这个接口。
- go 的垃圾回收机制是交给了编译器来做,虽然会有一些额外开销,但降低了开发难度。
-
import _ <package>
引入了包并执行了它的init()
函数。 - go 的找包过程是找 go 的安装目录
/usr/local/go
下和$GOPATH
下找的,所以如果go get
安装不成功,可以按包名与目录名的对应关系将包 copy 到对应目录下,也可以改一下GOPROXY
,参考。 -
go clean main.go
可以删除go build main.go
生成的 main 可执行文件。 -
go build ./package/path/...
三个点表示匹配所有字符串,即本级目录及以下所有的包。 -
go build .
表示以当前包名作为名称生成当前的可执行文件。 -
go doc [<package>|<builtin directive>]
生成文档,用于命令行查看 -
godoc -http=:8088
可生成本地所有包的文档,并映射到http
的8088
端口,用于浏览器查看 - 在项目中用
doc.go
写上包名,相当于一个空文件,可以添加注释用于注释专用文档,不过记得包内要写上包名。 - Golang 中 time.Time 类型的零值对应的时间戳是个负值,表示的是 0001年01月01日;而 mongoDB中 date 的零值对应的时间戳是0,表示的是 1970年01月01日。因此这点要注意引起的 bug。
- Golang 的 cast 包中,string 转换为 int 的方法,会将 “08”开头的字符串当做8进制处理,因而得到结果 0 或对应的8进制数据。
- Golang 的数组是值类型的,而不是指针类型,所以 arr2 = arr1 后,更新 arr1 中的一个元素,arr2对应的元素不改变。PHP 中也是不变的,但 javascript 不一样,js中的 array 是引用类型。
- 找代码库。golang 在 github 上建立了一个镜像库,如 https://github.com/golang/net 即是 https://golang.org/x/net 的镜像库,所以可以在
$GOPATH/src/golang.org/x
下git clone https://github.com/golang/net.git
。其他包一样。 -
:=
引发的作用域问题。赋值时,若用:=
那左边就最好 都是 未曾定义过的变量;否则,就考虑用=
。因为可能会引发变量的作用域问题:
package main
func main() {
var a int
if a, b := 2, 5; b == 5 {
println(a) // output: 2
}
println(a) // output: 0
}
-
for
和goroutine
。在for
内定义的变量,在goroutine
func
中直接使用可避免传入值错误的问题:
for _, item := range items {
val := item
go func() {
fmt.Println("----------------- item:", val) // 直接用 item 非预期
}()
}
- 浮点数比较不能用 == ,用 math.Fdim() 函数
- for...range... 可自动辨别中文,即用 rune 类型
- 切片是指向数组的指针。改动切片中某一个值,源数组的对应值会变。,只有当容量占满再 append 时,才会新建一个底层数组。
- 接口:非侵入性,实现了接口定义的方法,就是实现了接口。接口组合,也是这个特性,可以随意组合(两个接口放在组合接口中)。
- 通道:无缓冲、有缓冲。
- 有缓冲和无缓冲的差别:无缓冲就相当于手传手地递东西;有缓冲就相当于暂时放在缓冲池的空位里等待对方来取。
- 防并发,就是避免竞争,主要三种方式:
- 原子方法:atomic 包对于基本数据的原子操作,另外还有 sync.once() 方法让全局只执行一次。
- 加锁:暴力锁和委婉锁。
- 通道,交换数据。
- 通道的“有缓冲”和“无缓冲”这样理解:无缓冲就是双方手递手地传递物品,有缓冲时就可以临时放在缓冲区,满时放阻塞,空时取阻塞。
- 关闭通道,但通道中还有数据怎么办?关闭通道后,不能再放数据,但依然可以取用。所以
val, ok := <-chans
这句中 若 ok 为 false 即表示了没值,也表示通道被关闭了。因为没被关闭的话,会在阻塞状态。 - time.After() 函数常用于 timeout,它返回的一个
<-chan time.Time
,当过时了后才返回值,不过时不返回。 - “继承”的等效:嵌入类型。可以实现:
- 增加新属性,新方法
- “继承”方法
- “复写”方法
- 但嵌入类型没有接口中重载概念的等效
- 方法的接受者。用 值 类型定义的方法,可以值和引用调,但用 引用 类型定义的方法,只能用 引用类型 调用。因为有些值类型没有引用的地址。比如
Duration(6)
。 - golang 中, json 转 map,里面的数字是 float64 类型。
- 可以这样来取命令行输入
r := bufio.NewReader(os.Stadin)
...
b, _, _ := r.ReadLine()
line := string(b)
- 使用 map 时要注意 空映射 和 nil映射,他们不同
- os 包中这样定义 Stdin, Stdout, Stderr,这其实是 linux 的基础知识了,/dev/stdin 就是输入设备,就是键盘,/dev/stdout 就是标准输出设备,就是命令行
Stdin := NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout := NewFile(uintptr(syscall.Stdin), "/dev/stdout")
Stderr := NewFile(uintptr(syscall.Stdin), "/dev/stderr")
- json.Unmarshal() 会根据一个约定的顺序查找目标结构对应的字段。顺序如下:
- 包含 Foo 标签的字段
- 名为 Foo 的字段
- 首字母后面不区分大小写的 Foo 字段
- append 不是线程安全的,当并发时可能导致数据丢失
-
0 / 0
结果为NaN
;1 / 0
结果为+Inf