基础 Println 与 Printf 区别
Println: 可以打印出字符串,和变量fmt.Println(a)
Printf: 只可以打印出格式化的字符串,可以输出字符串类型的变量, 不可以输出整形变量和整形fmt.Printf("%d", a) // right
fmt.Println("abc") // right
Sprintf: 格式化之后的字符串
枚举 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func enums () { const ( a = iota _ b c ) const ( a = 10 * iota _ b c ) }
if 双重判断 1 2 3 4 5 6 const filename = "abc.txt" if contents, err := ioutil.ReadFile(filename); err == nil { fmt.Println(string (contents)) } else { fmt.Println(err) }
拿函数名称 1 2 p := reflect.ValueOf(function).Pointer() funcName := runtime.FuncForPC(p).Name()
指针 1 2 3 num := 1 func a (num *int ) {}a(&num)
数组(一般不用) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var arr1 [5 ]int arr1 := [3 ]{1 ,3 ,5 } arr2 := [...]int {2 , 4 , 6 , 8 , 10 } var grid [4 ][5 ]int for index, value := range arr { fmt.Println(i, v) } func printArr (arr *[5]int ) { fmt.Println(arr) } arr2 := [...]int {1 , 2 , 3 , 4 , 5 } printArr(&arr2)
切片 slice,数组的视图 要改变原数组,先转slice,下面操作后,原数组会被改变,slice 同样会被改变。 slice 为引用类型
1 2 3 4 func 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 5 arr := [...]int {0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 } s1 := arr[2 :6 ] s2 := s1[3 :5 ] fmt.Printf("s1=%v, len=%d, cap= %d \n" , s1, len (s1), cap (s1)) fmt.Printf("s1=%v, len=%d, cap= %d \n" , s2, len (s2), cap (s2))
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 2 s2 = s2[1 :] s2 = s2[:len (s2)-1 ]
map, [key]value 1 2 3 4 5 m := map [string ]string { "a" : "b" , } m1 := make (map [string ]int ) var m3 map [string ]int
遍历
1 2 3 for key, value := range m { fmt.Println(key, value) }
获取,不存在则为空
1 2 name, isTrue := m['a' ] if name, isTrue := m['a' ]; isTrue {} else {}
删除
delete(m, 'a')
个数
len(m)
Key 的类型
除了 slice, map, func 的内建类型
struct 类型不包含上述字段,编译时检查此项
字符串,rune 1 2 3 4 5 6 s := "中文字符串" len (s) == len ([]byte (s)) utf8.RuneCountInString(s) for index, zh := range []rune (s) { fmt.Println(index, zh) }
结构体 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type treeNode struct { value int left, right *treeNode } root = treeNode{value: 3 } root.right = &{5 , nil , nil } root.right.left = new (treeNode) var pnode *treeNode nodes := []treeNode { {value: 3 }, {}, {6 , nil , &root} }
工厂函数, 返回局部变量地址 不需要知道创建再堆还是栈
如果没有取地址返回,则在栈上,退出即回收
如果取地址返回,则在堆上,参与垃圾回收1 2 3 func createNode (value int ) *treeNode { return &treeNode{value: value} }
接收者调用,值传递,需要时传指针,使用时一样
1 2 3 4 5 6 7 8 func (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 21 type 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 3 func (mynode *node) post() { }
内嵌 相当于 node 平铺到点了 myNode 层
1 2 3 4 5 6 7 type 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 15 type P interface { Post() } type G interface { Get() } type Pg interface { P G } func test (r Pg) { r.Get() r.Post() }
panic
停止当前函数执行
一直向上返回,执行每一层的 defer
如果没有遇见 recover ,程序退出
1 2 3 4 5 6 7 8 9 10 11 func tryRecover () { defer func () { r := recover () if err, ok := r.(error ); ok { fmt.Println("error: " , err) } else { panic (r) } }() panic (errors.New("is error" )) }
表格驱动测试 样例
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 39 package mainimport ( 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" ) } } } func BenchmarkTestNewRespMsg (b *testing.B) { var tests = &util.RespMsg{-1 , "错误" , nil ,} b.Log(tests) 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 2 go test -coverprofile=c.outgo tool cover -html=c.out
协程
轻量级 “线程”
非抢占式多任务处理,由协程主动交出控制权
编译器/解释器/虚拟机层面的多任务
多个协程可能在一个或多个线程上运行
子程序是协程的一个特例
映射到物理线程执行,4核就占用4个,自动调度
主动交出控制
runtime.Gosched()
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func 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 2 3 4 5 6 7 8 9 10 11 12 13 func main () { var wg sync.WaitGroup wg.Add(1 ) go Run(&wg) wg.Wait() } func Run (wg *sync.WaitGroup) { fmt.Println("log" ) wg.Done() }
channel 不要通过共享内存来通信,通过通信来共享内存
https://www.jianshu.com/p/36e246c6153d
https://www.jianshu.com/p/a3c9a05466e1
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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 func worker (id int , c chan int ) { for n := range c { fmt.Printf("Worker %d received %d \n" , id, n) } } func createWorker (id int ) chan <- int { c := make (chan int ) go func () { for { fmt.Printf("Worker %d received %c \n" , id, <-c) } }() return c } func chanDemo () { var channels [10 ]chan <- int for i := 0 ; i<10 ; i++ { channels[i] = createWorker(i) } for i := 0 ; i<10 ; i++ { channels[i] <- 'a' + i } for i := 0 ; i<10 ; i++ { channels[i] <- 'A' + i } time.Sleep(time.Millisecond) } func bufferedChannel () { c := make (chan int , 3 ) go worker(0 , c) c <- 1 c <- 2 c <- 3 close (c) time.Sleep(time.Millisecond) } func main () { chanDemo() bufferedChannel() } 使用 `for v:= range c` 时,必须先关闭才能使用循环读取
等待多个 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 48 type 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) 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 2 3 4 5 6 7 8 var c1,c2 chan int select { case n:= <-c1: xxx case n:= <-c2: xxx default : }
go 函数中与外层通讯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 func 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 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 type Students struct { class string } type User struct { Name string Students } func main () { u := User{ Name: "test" , Students: Students{ class: "一班" , }, } check(&u) } func check (v interface {}) { t := reflect.TypeOf(v) t1 := reflect.ValueOf(v) tk := t.Kind() if tk == reflect.Struct { } else if tk == reflect.String { } else if tk == reflect.Ptr { e := t1.Elem() e.FieldByName("Name" ).SetString("test1" ) fmt.Println(v) } }
QA 声明的几种方式 struct
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 type node struct { value int left, right *node } var node1 nodenode1 = node{} fmt.Print(node1) fmt.Print("\n" ) var node2 *node fmt.Print(node2) fmt.Print("\n" ) var node3 = new (node)fmt.Print(node3)