go学习笔记
语法
特殊声明
_用法
用在 import
_ "github.com/go-sql-driver/mysql"
程序默认执行
init
方法用在函数返回值
_, err := client.Do(req)
忽略相应的返回值
new 函数
内建的new
函数也是一种创建变量的方法,new(type)
表示创建一个type
类型的匿名变量,并初始化为type
类型的零值,返回变量的地址,指针类型为*type
。
1 | p := new(int) // p, *int 类型, 指向匿名的 int 变量 |
如下函数完成同样的功能:创建变量,返回变量地址
1 | func newA() *int { |
基本类型
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8 的别名
rune // int32 的别名
// 表示一个 Unicode 码点
float32 float64
complex64 complex128
本例展示了几种类型的变量。 同导入语句一样,变量声明也可以“分组”成一个语法块。
命名返回值
没有参数的 return 语句返回已命名的返回值。也就是 直接 返回
1 | func method() (x, y int) { |
短变量声明
在函数中,简洁赋值语句 := 可在类型明确的地方代替 var 声明。
函数外的每个语句都必须以关键字开始(var, func 等等),因此 := 结构不能在函数外使用。
指针
指针声明:var p *int
指针赋值:var p *int = &i
or pp := &i
空指针:nil
指针使用:主要经过三个步骤:声明、赋值和访问指针指向的变量的值
1 | var i int = 10 |
结构体
1 | type Vertex struct { |
数组
数组声明及赋值
两种方式:
1 | // 第一种 |
遍历
1 | // 第一种 |
map
map 声明及赋值
1 | // 第一种 |
遍历
1 |
slice 切片
切片并不存储任何数据,它只是描述了底层数组中的一段。
更改切片的元素会修改其底层数组中对应的元素。
与它共享底层数组的切片都会观测到这些修改。
声明
1 | a := [5]int{1,2,3,4,5} |
用法
切片 s 的长度和容量可通过表达式 len(s)
和 cap(s)
来获取。
流程控制语句:for、if、else、switch 和 defer
for
1 | sum := 0 |
初始化语句和后置语句是可选的
1 | for ; sum < 1000; { |
for 是 Go 中的 “while”
1 | for sum < 10 { |
无限循环
1 | for { |
if
1 | if sum==0 { |
同 for 一样, if 语句可以在条件表达式前执行一个简单的语句。该语句声明的变量作用域仅在 if 之内。
1 | if sum = 1 ; sum==1 { |
defer
defer 语句会将函数推迟到外层函数返回之后执行。推迟调用的函数其参数会立即求值,但直到外层函数返回前该函数都不会被调用。 个人猜测用途是关闭资源,处理异常等。
推迟的函数调用会被压入一个栈中。当外层函数返回时,被推迟的函数会按照后进先出的顺序调用
1 | func main() { |
测试
go 默认有个轻量级测试框架,可以使用go test
命令和testing
包
创建一个文件,文件名以 _test.go
结尾,函数名为TestXXX
,并且传递参数(t *testing.T)
例如:
1 | package morestrings |
然后执行命令go test
方法和接口
函数与方法区别
方法在 func 关键字后是接收者而不是函数名
- 普通函数
1
2
3func function_name([parameter list]) [return_types] {
函数体
} - 方法(如 struct 方法)
1
2
3func (variable_name variable_data_type) method_name([parameter list]) [return_type]{
/* 函数体*/
}
使用区别
函数: function_name()
函数有参数的话,必须保持类型一致,否则编译失败。
方法:p.method_name()
其中 p 可以为指针,也可以为值(p 为结构体的值)
方法的接受者可以为指针,也可以为值。例如:
1 | func (v Vertex) Abs() float64 { |
使用指针接收者的原因有二:
方法能够修改其接收者指向的值。
这样可以避免在每次调用方法时复制该值。若值的类型为大型结构体时,这样做会更加高效。
接口
如果一个类型实现了一个接口需要的所有方法,那么该类型就实现了这个接口。
1 | type User struct { |
类型断言
类型断言 提供了访问接口值底层具体值的方式。t := i.(T)
1 | var a interface{} = 11 |
并发
使用go f(a)
即可新建一个 goroutine
信道
声明一个信道c := make(chan int)
带缓存的信道 c := make(chan int,10)
仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞。
通过 channel 可以实现唤醒线程,例如:
1 | go func() { |
range/close
只有发送者才能关闭信道
1 | ch := make(chan string) |
sync.Mutex
互斥锁
实现从 0 加到 1000
1 | type Account struct { |