go基礎語法50問,來看看你的go基礎合格了嗎?( 二 )

6.你是否主動關閉過http連接,為啥要這樣做有關閉,不關閉會程序可能會消耗完 socket 描述符 。有如下2種關閉方式:

  • 直接設置請求變量的 Close 字段值為 true,每次請求結束后就會主動關閉連接 。設置 Header 請求頭部選項 Connection: close,然后服務器返回的響應頭部也會有這個選項,此時 HTTP 標準庫會主動斷開連接
// 主動關閉連接func main() { req, err := http.NewRequest("GET", "http://golang.org", nil) checkError(err) req.Close = true //req.Header.Add("Connection", "close") // 等效的關閉方式 resp, err := http.DefaultClient.Do(req) if resp != nil {defer resp.Body.Close() } checkError(err) body, err := ioutil.ReadAll(resp.Body) checkError(err) fmt.Println(string(body))}
  • 你可以創建一個自定義配置的 HTTP transport 客戶端 , 用來取消 HTTP 全局的復用連接 。
func main() { tr := http.Transport{DisableKeepAlives: true} client := http.Client{Transport: &tr} resp, err := client.Get("https://golang.google.cn/") if resp != nil {defer resp.Body.Close() } checkError(err) fmt.Println(resp.StatusCode) // 200 body, err := ioutil.ReadAll(resp.Body) checkError(err) fmt.Println(len(string(body)))}7.解析 JSON 數據時 , 默認將數值當做哪種類型在 encode/decode JSON 數據時,Go 默認會將數值當做 float64 處理 。
func main() {var data = https://www.huyubaike.com/biancheng/[]byte(`{"status": 200}`)var result map[string]interface{}if err := json.Unmarshal(data, &result); err != nil {log.Fatalln(err)}解析出來的 200 是 float 類型 。
8.如何從 panic 中恢復在一個 defer 延遲執行的函數中調用 recover ,它便能捕捉/中斷 panic 。
// 錯誤的 recover 調用示例func main() { recover() // 什么都不會捕捉 panic("not good") // 發生 panic,主程序退出 recover() // 不會被執行 println("ok")}// 正確的 recover 調用示例func main() { defer func() {fmt.Println("recovered: ", recover()) }() panic("not good")}9.簡短聲明的變量需要注意啥
  • 簡短聲明的變量只能在函數內部使用
  • struct 的變量字段不能使用 := 來賦值
  • 不能用簡短聲明方式來單獨為一個變量重復聲明,:= 左側至少有一個新變量,才允許多變量的重復聲明
10.range 迭代 map是有序的嗎無序的 。Go 的運行時是有意打亂迭代順序的,所以你得到的迭代結果可能不一致 。但也并不總會打亂,得到連續相同的 5 個迭代結果也是可能的 。
11.recover的執行時機無 , recover 必須在 defer 函數中運行 。recover 捕獲的是祖父級調用時的異常,直接調用時無效 。
func main() {recover()panic(1)}直接 defer 調用也是無效 。
func main() {defer recover()panic(1)}defer 調用時多層嵌套依然無效 。
func main() {defer func() {func() { recover() }()}()panic(1)}必須在 defer 函數中直接調用才有效 。
func main() {defer func() {recover()}()panic(1)}12.閉包錯誤引用同一個變量問題怎么處理在每輪迭代中生成一個局部變量 i。如果沒有 i := i 這行,將會打印同一個變量 。
func main() {for i := 0; i < 5; i++ {i := idefer func() {println(i)}()}}或者是通過函數參數傳入 i。
func main() {for i := 0; i < 5; i++ {defer func(i int) {println(i)}(i)}}13.在循環內部執行defer語句會發生啥defer 在函數退出時才能執行,在 for 執行 defer 會導致資源延遲釋放 。
func main() {for i := 0; i < 5; i++ {func() {f, err := os.Open("/path/to/file")if err != nil {log.Fatal(err)}defer f.Close()}()}}func 是一個局部函數 , 在局部函數里面執行 defer 將不會有問題 。
14.說出一個避免Goroutine泄露的措施可以通過 context 包來避免內存泄漏 。
func main() {ctx, cancel := context.WithCancel(context.Background())ch := func(ctx context.Context) <-chan int {ch := make(chan int)go func() {for i := 0; ; i++ {select {case <- ctx.Done():returncase ch <- i:}}} ()return ch}(ctx)for v := range ch {fmt.Println(v)if v == 5 {cancel()break}}}下面的 for 循環停止取數據時,就用 cancel 函數,讓另一個協程停止寫數據 。如果下面 for 已停止讀取數據,上面 for 循環還在寫入,就會造成內存泄漏 。
15.如何跳出for select 循環通常在for循環中,使用break可以跳出循環,但是注意在go語言中,for select配合時,break 并不能跳出循環 。
func testSelectFor2(chExit chan bool){ EXIT:for{select {case v, ok := <-chExit:if !ok {fmt.Println("close channel 2", v)break EXIT//goto EXIT2}fmt.Println("ch2 val =", v)}}//EXIT2:fmt.Println("exit testSelectFor2")}

推薦閱讀