-
使用定時器
t := time.NewTicker(1 * time.Second) // 第一種方式 for { select { case <-t.C: fmt.Println("hello ") default: time.Sleep(100 * time.Millisecond) } } // 第二種方式 for _ = range t.C { fmt.Println("hello") } -
生成指定長度的隨機字符串
rand.Seed(time.Now().UnixNano()) data := make([]byte, 32) rand.Read(data) fmt.Println(hex.EncodeToString(data)) -
slice轉(zhuǎn)array
func main() { var arr [10]int s := []int{1, 2, 3, 4, 5} copy(arr[:], s) fmt.Println(arr) }原理解析:arr[:]是一個引用了底層數(shù)組arr的slice,長度和容量與數(shù)組的長度相同,也就是說這兩個元素共用一塊內(nèi)存。所以當copy()時,改變了slice中的元素值,而數(shù)組的改變可以說是一種副效應(side-effect)。
-
通過append函數(shù)刪除slice中的元素,是否會重新分配內(nèi)存的問題
package main import "fmt" func main() { s := []int{1, 2, 3, 4, 5} a := s fmt.Println(a) s = append(s[:1], s[2:]...) // 修改s[0],如果a的數(shù)據(jù)也變化了,那么說明通過append() // 刪除slice中的數(shù)據(jù)并沒有重新分配內(nèi)存空間 s[0] = 12 fmt.Println(a, s) } // output [1 2 3 4 5] [6 3 4 5 5] [6 3 4 5] -
交換兩個元素的值,而不需要中間變量
package main import "fmt" func main() { i := 1 j := 2 i = i ^ j j = i ^ j i = i ^ j fmt.Println(i, j) } // 規(guī)律是 i ^ i = 0 -
如果當前函數(shù)或者其調(diào)用棧上游,有處理panic的recover(),panic()就不會輸出,更不會終止程序,但是panic()的確執(zhí)行了。
package main import "fmt" func main() { test() fmt.Println("here is waiting") } func test() { defer func() { // 可以在panic()上游的任何位置定義,都會攔截 if r := recover(); r != nil { fmt.Println("is there") } }() base() } func base() { panic("this is a error") } -
從一個給定的slice獲取空的slice
package main import "fmt" func main() { s := []int{1, 2, 3, 4, 5} sEmpty := s[0:0] fmt.Println(sEmpty) } -
[]byte轉(zhuǎn)十六進制
b := []byte{12, 34, 89} // 以下會得到相同的輸出 fmt.Printf("%x\n", b) fmt.Println(hex.EncodeToString(b) -
大端法與小端法
給定一個十六進制數(shù)0xOA0B0C0D,以下給出了在兩種不同方法下內(nèi)存的排序方式,內(nèi)存地址從左向右依次增大。上方為小端法表示、下方為大端法表示。
概念:
- 大端法: 數(shù)字的高位和低位與內(nèi)存地址相反
- 小端法: 數(shù)字的高位和低位與內(nèi)存地址相同
比較好記的是大端法數(shù)字的書寫順序與內(nèi)存排列順序一致
![大端法、小端法][image-1]
在golang中將一個數(shù)字轉(zhuǎn)換成byte,在小端法中,會解析低內(nèi)存地址的一個byte,作為輸出.
func main() { var a uint32 = 0x0A0B0C0D fmt.Println(byte(a)) } -
slice copy時注意事項
第一種情況為空slice,因此在使用Copy()方法時,目標slice長度一定要指定為需要copy的長度
func main() { s := make([]int, 0, 5) // error //s := make([]int, 4) // correct 4 or 5 ok c := []int{1, 2, 3, 4, 5} copy(s, c) fmt.Println(s) } -
標識符沖突
同一個包內(nèi)不能出現(xiàn)變量名、常量名、函數(shù)名兩兩沖突的情況,如下情況編譯失敗:
package main import "fmt" const conflict = 38 var conflict = 34 func conflict() { fmt.Println(conflict) } func main() { fmt.Println(conflict) conflict() } -
求兩個時間的間隔,會參數(shù)負數(shù)的情況
func main() { t1 := time.Now() t2 := time.Now() fmt.Println(t1.Sub(t2)) // 負數(shù) t3 := time.Unix(math.MaxInt64, 0) // 屬于越界的情況 fmt.Println(t3.Sub(t1)) } // output -657ns -2562047h47m16.854775808s所以在使用time包的Sub()函數(shù)時,要將結(jié)果與0進行比較
now := time.Now()
lastAttempt := now.Sub(ka.lastattempt) // 如果差值過大,就會形成負值if lastAttempt < 0 { lastAttempt = 0 } -
nil slice可以直接使用append()增加元素
func main() { var b []byte source := []byte{1, 2, 3} b = append(b, source...) fmt.Println(b) } -
golang set循環(huán)控制及返回值
func (ba *BlockAssembler) isStillDependent(te *mempool.TxMempoolEntry) bool { setParent := blockchain.GMemPool.GetMemPoolParents(te) ret := false setParent.Each(func(item interface{}) bool { if !ba.inBlock.Has(item.(*mempool.TxMempoolEntry)) { ret = true // control function result return false // control loop } return true // control loop }) return ret // return function result } -
select默認是堵塞的,必須有一個分支執(zhí)行才能出去。
func main() { a := make(chan int, 1) c := make(chan int, 1) go func() { time.Sleep(1 * time.Second) a <- 1 c <- 2 }() // for next { select { case <-a: fmt.Println("a") case <-c: fmt.Println("c") } } // 如果所有通道中都沒有內(nèi)容,可以使用default語句,繼續(xù)程序的執(zhí)行。不過要看業(yè)務需求。 func main() { a := make(chan int, 1) c := make(chan int, 1) go func() { time.Sleep(1 * time.Second) a <- 1 c <- 2 }() // for next { select { case <-a: fmt.Println("a") case <-c: fmt.Println("c") default: fmt.PrintLn("default") } } -
switch結(jié)構(gòu)中default語句的執(zhí)行與其所在位置無關
package main import "fmt" func main() { a := 232 switch a { default: fmt.Println("default") case 1: fmt.Println(1) case 232: fmt.Println("ok") } } -
遍歷一個無緩沖的channel,不會在從channel中讀取一個元素就退出(for range 結(jié)構(gòu)); for range 的終止條件是通道關閉,和channel有無緩沖無關。
package main import ( "fmt" "time" ) func main() { c := make(chan int) go func() { i := 0 for { c <- i i++ time.Sleep(1 * time.Second) } }() go func() { for v := range c { fmt.Println(v) } }() time.Sleep(10 * time.Second) close(c) } -
len()函數(shù)在channel的應用。當chanel為無緩沖的時候,len()在任何情況下都會返回0; 當channel為有緩沖的情況下,len()會返回當前緩沖中的元素個數(shù),增大或減小取決于生產(chǎn)者和消費者的速度??傊?,len()函數(shù)在channel中返回的是緩沖區(qū)的元素個數(shù)。
package main import ( "fmt" "time" ) func main() { c := make(chan int, 5) // 修改為無緩沖的channel嘗試一下 // producer go func() { i := 0 for { c <- i i++ time.Sleep(1 * time.Second) } }() // consumer go func() { for v := range c { fmt.Println(v) time.Sleep(2 * time.Second) } }() // watcher go func() { for { fmt.Println("channel length:", len(c)) time.Sleep(500 * time.Millisecond) } }() time.Sleep(30 * time.Second) } -
goto、continue、break語句實現(xiàn)代碼跳轉(zhuǎn)
// 既可以往前跳,也可以往后跳 func main() { previous: fmt.Println("previous") i := 0 if i == 0 { goto next } next: fmt.Println("next") goto previous } // break跳轉(zhuǎn)至指定標簽后,將不執(zhí)行標簽對應的for循環(huán)。需要注意的是:標簽只能定義在for循環(huán)之前。 func main() { i := 0 loop: for i <= 10 { i++ fmt.Println("break") break loop } fmt.Println("finish, and i ==", i) } // output: break finish, and i == 1 // continue跳轉(zhuǎn)到指定標簽后,如果for循環(huán)條件滿足仍然會執(zhí)行for循環(huán),直到條件不滿足為止。需要注意的是:標簽只能定義在for循環(huán)之前。 func main() { i := 0 loop: for i <= 10 { i++ fmt.Println("continue") continue loop } fmt.Println("finish, and i ==", i) } // output: continue continue continue continue continue continue continue continue continue continue continue finish, and i == 11 -
switch case for type asert:
func main() { var i interface{} a := 3 i = a switch t := i.(type) { case int: fmt.Println(t) default: fmt.Println("default") } } //output: 3 -
<- c被觸發(fā)的條件:1、通道中有數(shù)據(jù)
2、通道被關閉
func main() { c := make(chan struct{}) //c := make(chan struct{}, 100) go func() { time.Sleep(5 * time.Second) close(c) }() for { if interruptRequest(c) { break } time.Sleep(1 * time.Second) } } func interruptRequest(interrupted <-chan struct{}) bool { select { case <-interrupted: fmt.Println("program is down....") return true default: fmt.Println("program is running...") } return false } -
函數(shù)內(nèi)對slice作append,在函數(shù)外不可見: slice的元信息是SliceHeader,slice按值傳遞和按引用傳遞其實就是將SliceHeader按值傳遞或按指針傳遞。
func main() { s := make([]int, 0) a := (*reflect.SliceHeader)(unsafe.Pointer(&s)) fmt.Printf("%+v\n", a) test(s) b := (*reflect.SliceHeader)(unsafe.Pointer(&s)) fmt.Printf("%+v\n", b) } func test(s []int) { s = append(s, 1) a := (*reflect.SliceHeader)(unsafe.Pointer(&s)) fmt.Printf("%+v\n", a) } // output: &{Data:18193560 Len:0 Cap:0} &{Data:842350567624 Len:1 Cap:1} &{Data:18193560 Len:0 Cap:0} // ----------------------------------------------------- // 通過傳slice指針,實現(xiàn)對slice的修改 func main() { s := make([]int, 0) a := (*reflect.SliceHeader)(unsafe.Pointer(&s)) fmt.Printf("%+v\n", a) test(&s) b := (*reflect.SliceHeader)(unsafe.Pointer(&s)) fmt.Printf("%+v\n", b) } func test(s *[]int) { *s = append(*s, 1) a := (*reflect.SliceHeader)(unsafe.Pointer(s)) fmt.Printf("%+v\n", a) } // output: &{Data:18193560 Len:0 Cap:0} &{Data:842350567624 Len:1 Cap:1} &{Data:842350567624 Len:1 Cap:1} -
json自定義序列化
對于使用結(jié)構(gòu)體中嵌套結(jié)構(gòu)體的情況,只有receiver為指針類型,而嵌套結(jié)構(gòu)體為結(jié)構(gòu)體的值語義的時候不能觸發(fā)自定義Json格式化函數(shù)MarshalJSON;其他三種組合均能夠觸發(fā)。
對于使用結(jié)構(gòu)體中嵌套結(jié)構(gòu)體slice的情況,receiver值語義、指針語義和嵌套結(jié)構(gòu)體slice元素為值語義、指針語義的四種組合均能夠觸發(fā)Json格式化函數(shù)MarshalJSON。
package main import ( "encoding/json" "fmt" ) type Profile struct { Level string Admin bool } // 本質(zhì)是將Profile指針類型實現(xiàn)Marshaler接口,從而達到自定義json序列化格式的目的。 func (p *Profile) MarshalJSON() ([]byte, error) { if p.Admin { admin := struct { Level string }{ Level: "admin", } return json.Marshal(admin) } control := struct { Level string Admin bool }{ Level: "control", Admin: false, } return json.Marshal(control) } type User struct { Id int Name string Age uint8 Profile *Profile } func main() { u := User{ Id: 1, Age: 23, Name: "qshuai", Profile: &Profile{ Level: "master", Admin: true, }, } b, err := json.Marshal(u) if err != nil { panic(err) } fmt.Println(string(b)) } // -----------------------------slice作為Struct成員的情況---------------------------- package main import ( "encoding/json" "fmt" ) type Profile struct { Level string Admin bool } func (p *Profile) MarshalJSON() ([]byte, error) { if p.Admin { admin := struct { Level string }{ Level: "admin", } return json.Marshal(admin) } control := struct { Level string Admin bool }{ Level: "control", Admin: false, } return json.Marshal(control) } type User struct { Id int Name string Age uint8 Profile []Profile } func main() { u := User{ Id: 1, Age: 23, Name: "qshuai", Profile: []Profile{ { Level: "master", Admin: true, }, }, } b, err := json.Marshal(u) if err != nil { panic(err) } fmt.Println(string(b)) } -
使用map[string]interface{}接收不確定json數(shù)據(jù)
data := []byte(` { "address": "16WtgAckGAYLHaxJnRFV5mueF8gaEbKc4W", "received": {"name":"qshuai", "age":23}, "sent": ["abc", 124, "def"] }`) var d map[string]interface{} err := json.NewDecoder(bytes.NewReader(data)).Decode(&d) if err != nil { panic(err) } spew.Dump(d) // output (map[string]interface {}) (len=3) { (string) (len=7) "address": (string) (len=34) "16WtgAckGAYLHaxJnRFV5mueF8gaEbKc4W", (string) (len=8) "received": (map[string]interface {}) (len=2) { (string) (len=4) "name": (string) (len=6) "qshuai", (string) (len=3) "age": (float64) 23 }, (string) (len=4) "sent": ([]interface {}) (len=3 cap=4) { (string) (len=3) "abc", (float64) 124, (string) (len=3) "def" } } -
不可尋址的情況
常量
map的值且值為值類型
函數(shù)返回值并且返回值為值類型
接口斷言且斷言成值類型成功
函數(shù)返回值為數(shù)組類型(非數(shù)組指針類型),對返回值直接做[m:n]取slice的操作是不合法的
-
賦值簡寫方式的坑
// 1、同一作用域 := 左側(cè)至少有一個未申明 // 2、同一作用域 := 左側(cè)重復的變量名的變量地址是相同的 package main import "fmt" func main() { a, b := 1, 2 fmt.Printf("%p\n", &a) a, c := 2, 3 fmt.Printf("%p\n", &a) fmt.Println(b, c) } -
for…range 坑
package main import "fmt" type T struct { ID int } func (t *T) PrintID() { fmt.Println(t.ID) } func F1() { ts := []T{{1}, {2}, {3}} for _, t := range ts { // 每次遍歷,將ts中的相應的元素拷貝到臨時變量t,同一個迭代的t的內(nèi)存地址是相同的 defer t.PrintID() // 先取出t的地址,然后壓棧 } } func F2() { ts := []*T{&T{1}, &T{2}, &T{3}} for _, t := range ts { defer t.PrintID() } } func main() { fmt.Println("F1()") F1() fmt.Println() fmt.Println("F2()") F2() } == nil的判斷結(jié)果: 如果左側(cè)為接口類型,那么只有接口變量的值和類型均為nil的情況下,== nil才能成立;如果左側(cè)是普通數(shù)據(jù)類型,那么只要其變量的值為nil,那么== nil就會成立。以
Example_開頭的函數(shù)會在go test時自動運行。golang中的引用類型:
slice、map、channel、function、interface、pointer-
nil指針可以調(diào)用自己的方法,但是不能在函數(shù)內(nèi)對指針進行解引用或者訪問其成員變量
package main import "fmt" type user struct { ID int Name string } func (u *user) getInfo() { fmt.Println("hello world") // fmt.Println(u.Name) // will panic } func main() { var u *user if u == nil { fmt.Println("u == nil") } u.getInfo() // ok } -
nil slice除了不能使用索引訪問外,其他操作都是可以的
package main import "fmt" func main() { var s []int s[0] = 1 // panic fmt.Println(s[1]) // panic fmt.Println(len(s), cap(s)) // 0 0 for index, item := range s { // nothing fmt.Println(index, item) } s = append(s, 1) fmt.Println(s) // [1] } -
nil map除了不能寫入元素外,其他操作都是允許的(可以把nil map看成只讀的map)
package main import "fmt" func main() { var m map[int]string fmt.Println(m == nil) // true fmt.Println(len(m)) // 0 for key, value := range m { // nothing fmt.Println(key, value) } item, ok := m[2] fmt.Println(item, ok) // false m[0] = "qshuai" // panic m1 := map[int]string{} fmt.Println(m1 == nil) // false m1[1] = "qshuai" fmt.Println(m1[1]) // qshuai } -
nil channel的寫入和讀取都會造成死鎖,而關閉一個nil channel會引起panic
func main() { var c chan int fmt.Println(c == nil) // true <-c // deadlock c <- 3 // deadlock close(c) // panic }已關閉的channle仍然可以被讀取,只不過結(jié)果總為對應類型的零值
func main() { cc := make(chan int) go func() { stop: for { select { case v, ok := <-cc: if ok { fmt.Println(v) } else { break stop // will not into for loop } } } }() go func() { var i int for { cc <- i i++ time.Sleep(500 * time.Millisecond) if i > 5 { close(cc) break } } }() time.Sleep(10 * time.Second) } -
switch中的fallthrough用法:fallthrough將執(zhí)行下一個case內(nèi)的語句, 而不用判斷case的條件是否滿足。func main() { i := 3 switch i { default: fallthrough case 1: fmt.Println("ok") case 2: fmt.Println("false") } } // output: ok -
switch case的高級用法
func isAcceptableKind(kind reflect.Kind) bool { switch kind { case reflect.Chan: fallthrough case reflect.Complex64: fallthrough case reflect.Complex128: fallthrough case reflect.Func: fallthrough case reflect.Ptr: fallthrough case reflect.Interface: return false } return true } -
slice用法
data[:6:8] 每個數(shù)字前都有個冒號, slice內(nèi)容為data從0到第6位,長度len為6,最大擴充項cap設置為8
-
檢查關閉通道
w.quitMu.Lock() // select 中只有一個通道操作,之所有用select是因為,select可以通過default的方式避開 // 堵塞的情況 select { case <-w.quit: w.quitMu.Unlock() return default: }