基础
Println 与 Printf 区别
- Println: 可以打印出字符串,和变量
fmt.Println(a)
- Printf: 只可以打印出格式化的字符串,可以输出字符串类型的变量, 不可以输出整形变量和整形
fmt.Printf("%d", a) // right
fmt.Println("abc") // right
- Sprintf: 格式化之后的字符串
枚举
1 | func enums() { |
if 双重判断
1 | const filename = "abc.txt" |
拿函数名称
1 | p := reflect.ValueOf(function).Pointer() |
指针
1 | num := 1 |
数组(一般不用)
1 | var arr1 [5]int |
切片 slice,数组的视图
要改变原数组,先转slice,下面操作后,原数组会被改变,slice 同样会被改变。
slice 为引用类型
1
2
3
4func p(s []int) {
s[0] = 100
}
p(arr[:])
slice 属性有3个
- prt 指向数组第一个
- len slice 长度
- cap slice 的底层数组
slice 为cap的切片
slice 可以切到后面的数据(cap),不能向前切
下标取值不可超过len(s1)
,向后切不可超过cap(s1)
1
2
3
4
5arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
s2 := s1[3:5] // 可以取到后面的值,但是s1[4]取不到
fmt.Printf("s1=%v, len=%d, cap= %d \n", s1, len(s1), cap(s1)) // s1=[2 3 4 5], len=4, cap= 6
fmt.Printf("s1=%v, len=%d, cap= %d \n", s2, len(s2), cap(s2)) // s1=[5 6], len=2, cap= 3 这里cap包含了 7
1 | len(s1) // slice 长度 |
append
- append 超过 view 层则替换 原cap数组 数据,如果大于 cap 长度则开一个更大的 Array,原 cap 数组不会新增,可能被垃圾回收。
- 需要一个值来接受append的返回值
- len 将大于cap时,会拷贝一份slice,并添加新的数据
- len可以一个一个增长,cap会为2的次方数,并且大于len
新建数组
var s []int
新建len=16的数组,被 0 填充
s2 := make([]int, 16)
新建len=16的数组,cap=32
s3 := make([]int, 16, 32)
copy
copy(s2, s1)
// s1复制到s2,s2的前面几位变成s1,
删除,s2截取 0 - 2位 + 4 - 结束
s2 = append(s2[:3], s2[4:]...)
去掉头,尾
1
2s2 = s2[1:]
s2 = s2[:len(s2)-1]
map, [key]value
1 | m := map[string]string { |
遍历
1
2
3for key, value := range m {
fmt.Println(key, value)
}
获取,不存在则为空
1
2name, isTrue := m['a'] // 第二个参数返回是否存在
if name, isTrue := m['a']; isTrue {} else {}
删除
delete(m, 'a')
个数
len(m)
Key 的类型
- 除了 slice, map, func 的内建类型
- struct 类型不包含上述字段,编译时检查此项
字符串,rune
1 | s := "中文字符串" |
结构体
1 | type treeNode struct { |
工厂函数, 返回局部变量地址
不需要知道创建再堆还是栈
- 如果没有取地址返回,则在栈上,退出即回收
- 如果取地址返回,则在堆上,参与垃圾回收
1
2
3func createNode(value int) *treeNode {
return &treeNode{value: value}
}
接收者调用,值传递,需要时传指针,使用时一样
1
2
3
4
5
6
7
8func (node s) print () {}
s.print()
func print (node s) {}
print(s)
func (*node s) print () {}
s.print()
扩展类型
定义别名1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21type node struct {
value int
left, right *node
}
type myNode struct {
node *node
}
func (theNode *myNode) post() {
if theNode == nil || theNode.node == nil {
return
}
fmt.Print("post")
}
func main() {
var root node
myRoot := myNode{&root}
myRoot.post()
}
使用组合 (最常用)
1
2
3func (mynode *node) post() {
// 改变mynode的值
}
内嵌
相当于 node 平铺到点了 myNode 层
1
2
3
4
5
6
7type myNode struct {
*node
}
func (theNode *myNode) post() {
fmt.Print(theNode.left)
}
接口
任何类型type Queue []interface{}
强制转换interface()head.(int)
组合1
2
3
4
5
6
7
8
9
10
11
12
13
14
15type P interface {
Post()
}
type G interface {
Get()
}
type Pg interface {
P
G
}
func test (r Pg) {
r.Get()
r.Post()
}
panic
- 停止当前函数执行
- 一直向上返回,执行每一层的 defer
- 如果没有遇见 recover ,程序退出
1 | func tryRecover() { |
表格驱动测试
样例
resp_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39package main
import (
util "server/util"
"testing"
)
func TestNewRespMsg(t *testing.T) {
var tests = &[]util.RespMsg{
{
-1, "错误", nil,
},
}
for _, tt := range *tests {
data := *util.NewRespMsg(tt.Code, tt.Msg, tt.Data)
data1 := util.RespMsg{ -1, "错误", nil, }
if (data != data1) {
t.Errorf("error")
}
}
}
// out: BenchmarkTestNewRespMsg-12 66670740 18.3 ns/op
// 运行 66670740 次,每次耗时 18.3ns
func BenchmarkTestNewRespMsg(b *testing.B) {
var tests = &util.RespMsg{-1, "错误", nil,}
b.Log(tests)
// 上面的生成不算时间,有 for 循环等情况使用
b.ResetTimer()
for i:= 0; i<b.N; i++ {
data := *util.NewRespMsg(tests.Code, tests.Msg, tests.Data)
data1 := util.RespMsg{ -1, "错误", nil, }
if (data != data1) {
b.Errorf("error")
}
}
}
代码覆盖率
1
2go test -coverprofile=c.out
go tool cover -html=c.out
协程
- 轻量级 “线程”
- 非抢占式多任务处理,由协程主动交出控制权
- 编译器/解释器/虚拟机层面的多任务
- 多个协程可能在一个或多个线程上运行
- 子程序是协程的一个特例
- 映射到物理线程执行,4核就占用4个,自动调度
主动交出控制
runtime.Gosched()
例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14func main() {
var a [1000]int
for i:= 0; i<1000; i++ {
go func(i int) {
for {
a[i]++
runtime.Gosched()
}
}(i)
}
time.Sleep(time.Millisecond)
fmt.Println(a)
}
可能切换的点,只是参考,不能保证在其他地方不切换
- I/O, select: print,读写文件等
- channel
- 等待锁
- 函数调用(可能)
- runtime.Gosched()
等待协程
1 | func main() { |
channel
不要通过共享内存来通信,通过通信来共享内存
https://www.jianshu.com/p/36e246c6153d https://www.jianshu.com/p/a3c9a05466e1
1 | func worker(id int, c chan int) { |
等待多个 goroutine 结束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48type worker struct {
in chan int
done func()
}
func doWorker(id int, w worker) {
for n := range w.in {
fmt.Printf("Worker %d received %c \n",
id, n) // <-c 读取
w.done()
}
}
func createWorker(id int, wg *sync.WaitGroup) worker {
w := worker {
in: make(chan int),
done: func() {
wg.Done()
},
}
go doWorker(id, w)
return w
}
func chanDemo() {
// 等待多个任务做完
var wg sync.WaitGroup
var workers [10]worker
for i := 0; i<10; i++ {
workers[i] = createWorker(i, &wg)
}
wg.Add(20)
for i, worker := range workers {
worker.in <- 'a' + i
}
for i, worker := range workers {
worker.in <- 'A' + i
}
wg.Wait()
}
func main() {
chanDemo()
}
select 谁来的快接受谁的参数
1 | var c1,c2 chan int |
go 函数中与外层通讯
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22func main() {
c := make(chan int)
var readc <-chan int = c
var writec chan<- int = c
go SetChan(writec)
GetChan(readc)
}
func SetChan(writec chan<- int) {
for i := 0; i < 10; i++ {
fmt.Println("set \n")
writec <- i
}
}
func GetChan(writec <-chan int) {
for i := 0; i < 10; i++ {
fmt.Println("get \n")
}
}
断言 反射
1 | type Students struct { |
QA
声明的几种方式
struct
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16type node struct {
value int
left, right *node
}
var node1 node
node1 = node{}
fmt.Print(node1) // {0 <nil> <nil>} 声明,赋值
fmt.Print("\n")
var node2 *node
fmt.Print(node2) // <nil> 仅声明
fmt.Print("\n")
var node3 = new(node)
fmt.Print(node3) // &{0 <nil> <nil>} 声明,赋值,返回地址