一个测试

看到晨梦思雨老哥写的一个go语法50问,咱总称自己很懂go,语法总不能还不过关吧,所以当即决定测一下。

https://blog.csdn.net/ma2595162349/article/details/115255041

1.使用值为 nil 的 slice、map会发生啥

slice是允许给nil slice添加值的,但是不能直接索引赋值,会提示index out of slice

map的话,不可以直接操作

2.访问 map 中的 key,需要注意啥

没什么好注意的,只要知道其实是返回两个值就好
if v, ok := m["first"]; ok {
    fmt.Println(v)
}

3.string 类型的值可以修改吗

这大概牵扯到slice 的指针字段这种概念了。
老哥一个例子不错,会用避坑就好了。
// 修改字符串的错误示例
func main() {
 x := "text"
 x[0] = "T"  // error: cannot assign to x[0]
 fmt.Println(x)
}
 
 
// 修改示例
func main() {
 x := "text"
 xBytes := []byte(x)
 xBytes[0] = 'T' // 注意此时的 T 是 rune 类型
 x = string(xBytes)
 fmt.Println(x) // Text

————————————————
版权声明:本文为CSDN博主「晨梦思雨」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ma2595162349/article/details/115255041

4.switch 中如何强制执行下一个 case 代码块

go中不需要case-break,可能是为了防止不必要的case穿透,但是也可以加上fallthrough强制执行

5.你是如何关闭 HTTP 的响应体的

因人而异,感觉defer用不用都无所谓,只要别忘了关就好,还有为什么推荐用defer,这里有一个tcp链接的坑,不知老哥会不会补充
	resp, err := http.Get("http://www.baidu.com")
	if err != nil {
		return
	}
	defer resp.Body.Close()

6.你是否主动关闭过http连接,为啥要这样做

咱还真不怎么关注主动关闭这点,一直感觉keepalive就ok了。实际上咱也懂一些网络协议,可以简单分析一下。
KeepAlive带来的好处是可以减少HTTP连接的开销,提高性能。
KeepAlive带来的坏处当然是多客户端连接,又都长期占有连接,程序可能会消耗完 socket 描述符。

还有一点,哪边发起关闭连接,哪边会有TIME_WAIT,所以这个最好是客户端主动。

关闭的方式?
HTTP/1.1 defines the "close" connection option for the sender to signal that the connection will be closed after completion of the response. For example, 
       Connection: close 
       
func main() {
	req, err := http.NewRequest("GET", "http://www.baidu.com", nil)
	if err != nil {
		return
	}

	// method 1
	req.Close = true

	// method 2
	//req.Header.Add("Connection", "close")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	b, err := ioutil.ReadAll(resp.Body)
	
	fmt.Println(string(b))
}

创建一个自定义配置的 HTTP transport 客户端,用来取消 HTTP 全局的复用连接。
func main() {
	tr := http.Transport{DisableKeepAlives: true}
	client := http.Client{Transport: &tr}

	resp, err := client.Get("http://www.baidu.com")
	...
}

7.解析 JSON 数据时,默认将数值当做哪种类型

说实话,老哥这有点为难人,不过我根据经验推导出是float64,答案果然是,但是还是验证一下了。
func main() {
	var data = []byte(`{"age": 12}`)
	var res map[string]interface{}

	if err := json.Unmarshal(data, &res); err != nil {
		fmt.Println(err)
	}
	
	fmt.Println(reflect.TypeOf(res["age"]))		// float64
}

8.如何从 panic 中恢复

测试开发环境,我个人推荐直接panic,这也符合go的设计哲学,遇事不决,设计哲学。但是生产一定得recover捕捉啊,一个goroutine的panic也会导致整个程序挂掉。
	defer func() {
		if e := recover(); e != nil {
			fmt.Println("recover")
		}
	}()

9.简短声明的变量需要注意啥

已经过24点了,有点困了,怎么才第九个,下面快速过了。
这个记住一个坑,struct的字段不能直接 := 
比如people1.age := 12 (wrong)
为什么,应该讲你在这个属于声明,而你想做的是给people1.age赋值,赋值和声明不是一回事

那为什么可以 a := 1 这里是声明加赋值,你再 a := 2 就报错了,因为声明只能一次,所以以后只能 a = 2

10.range 迭代 map是有序的吗

无序,你去看下汇编会定位到一个rand index的

11.recover的执行时机

不能直接调用,必须在 defer 函数中运行,而且不能嵌套

12.闭包错误引用同一个变量问题怎么处理

func main() {
    for i := 0; i < 5; i++ {
        i := i
        defer func() {
            println(i)
        }()
    }
}
func main() {
    for i := 0; i < 5; i++ {
        defer func(i int) {
            println(i)
        }(i)
    }
}

13.在循环内部执行defer语句会发生啥

函数退出时延迟执行呗,还能发生啥。

defer咱就正常用好吧,这里面的坑太多,不清楚为什么很多人喜欢问defer执行顺序。

14.说出一个避免Goroutine泄露的措施

不用goroutine!这不是我说的,Davey Cheney说的,简单来讲就是不会用,水平不够咱就别用。

写完代码 go race 一下
用context设置一下cancel
尽量的咱用缓冲channel
测试环境pprof
...

15.如何跳出for select 循环

哈哈,意不意外,居然没法break,这个设计一开始也不懂,后来看了优先级select后突然明白了妙处

这里的答案是goto label 或者 break label

16.如何在切片中查找

放个截图吧

一个测试

17.如何初始化带嵌套结构的结构体

就那样初始化呗,嵌套的你就当一个对象“字段”

18.切片和数组的区别

除非这种需要 a := [3]int{}, 否则直接slice,其他写法也都是slice

区别在于slice有一个数组指针,len,cap字段,还有一些扩容操作啊(这是一个小坑)

19.new和make的区别

老哥怎么也出这么题,这俩就不是一个用途,问区别有啥意思啊。

new 返回一个指针

make主要用于channel slice map的创建,困死不想多写了。

20.Printf()、Sprintf()、Fprintf()函数的区别用法是什么

有点不太想写了,老哥的问题越来越没意思了。
        fmt.Println(fmt.Sprintf("hello %s", "world"))
	fmt.Fprintln(os.Stderr, "error!!!")

21.说说go语言中的for循环

break 和continue正常

不过有一些坑,比如遍历map,slice,其实看汇编,长度已经确定,所以for append不会无限循环

22.Array 类型的值作为函数参数

// 数组使用值拷贝传参
func main() {
 x := [3]int{1,2,3}
 
 func(arr [3]int) {
  arr[0] = 7
  fmt.Println(arr) // [7 2 3]
 }(x)
 fmt.Println(x)   // [1 2 3] // 并不是你以为的 [7 2 3]
}

// 传址会修改原数据
func main() {
 x := [3]int{1,2,3}
 
 func(arr *[3]int) {
  (*arr)[0] = 7 
  fmt.Println(arr) // &[7 2 3]
 }(&x)
 fmt.Println(x) // [7 2 3]
}

23.说说go语言中的switch语句

fallthrough

24.说说go语言中有没有隐藏的this指针

没有

25.go语言中的引用类型包含哪些

map slice channel interface

26.go语言中指针运算有哪些

& *  go不允许一些指针运算的,但是可以unsafe使用

26.说说go语言的main函数

主协程 mo go

27.go语言触发异常的场景有哪些

越界
空指针
除数0
panic

28.说说go语言的beego框架

29.说说go语言的goconvey框架

单元测试
断言
能web ui

30.GoStub的作用是什么

打桩

31.go语言编程的好处是什么

没有好处

32.说说go语言的select机制

for-select 异步io
每个case都需要当作一个io操作

33.解释一下go语言中的静态类型声明

编译的时候用。

动态语言一时超爽

34.go的接口是什么

非侵入式

不懂泛型的码农喜欢自以为是的用interface充当泛型使用hhh

35.Go语言里面的类型断言是怎么回事

x.(type)

36.go语言中局部变量和全局变量的缺省值是什么

类型零值

37.go语言编程的好处是什么

重复了啊

38.解释一下go语言中的静态类型声明

???

39.模块化编程是怎么回事

go以文件夹为模块,千万别再用MVC那种东西了

40.Golang的方法有什么特别之处

多返回值,可以返回指针(注意内存逃逸,Rust就不允许h

41.Golang可变参数

func test(a ...interface{}) {}

42.Golang Slice的底层实现

数组指针
cap
len

43.Golang Slice的扩容机制,有什么注意点

Go 中切片扩容的策略是这样的:

首先判断,如果新申请容量大于 2 倍的旧容量,最终容量就是新申请的容量。

否则判断,如果旧切片的长度小于 1024,则最终容量就是旧容量的两倍。

否则判断,如果旧切片长度大于等于 1024,则最终容量从旧容量开始循环增加原来的  1/4 , 直到最终容量大于等于新申请的容量。

如果最终容量计算值溢出,则最终容量就是新申请容量。

!!!!!!!!!复制Slice,使用Copy(性能已经不错了)~~~~~~~~~~

44.Golang Map底层实现

底层散列表,map还是有一些复杂了,困就一个字,不写了
  1. JSON 标准库对 nil slice 和 空 slice 的处理是一致的吗
肯定不一致啊

46.Golang的内存模型,为什么小对象多了会造成gc压力

看下Tmalloc吧

47.Data Race问题怎么解决?能不能不加锁解决这个问题

发现问题,只能证明存在问题,不能确保解决问题和没有问题。

go test -race mypkg    // 测试包
go run -race mysrc.go 

48.在 range 迭代 slice 时,你怎么修改值的

通过索引改

49.nil 和 nil interface 的区别

记住一点,比较要比较类型和值,两者一个不一致,就不相等

50.select可以用于什么

可以吃饭睡觉打豆豆

清楚 异步和阻塞就ok了

怎么说呢,前面感觉还有点意思,后面就感觉差回事了。

上一篇:var new 和 make的区别


下一篇:Go语言基础 - 切片(slice)