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")}

推荐阅读