当前位置:   article > 正文

Go面试:面试题笔记整理(一)_go语言 接口 只要两个接口拥有相同的方法列表(次序不同不要紧),那么它们就是等价

go语言 接口 只要两个接口拥有相同的方法列表(次序不同不要紧),那么它们就是等价

 对《Golang精编100题-搞定golang面试》里面的部分题目做一些笔记备查。


33. 【中级】 golang中的引用类型包括()
A. 数组切片
B. map
C. channel
D. interface

参考答案:ABCD
这道题已经过时了,在2013年4月3日的github提交中已经明确说了“Go has no 'reference types'”。而slice源码的说明也由“引用”改成了“描述符”。Go只有值传递,没有所谓的引用传递。上题应该改为哪些类型包含有指针。


34. 【中级】 golang中的指针运算包括()
A. 可以对指针进行自增或自减运算
B. 可以通过“&”取指针的地址
C. 可以通过“*”取指针指向的数据
D. 可以对指针进行下标运算

参考答案:BC


36. 【中级】下面赋值正确的是()
A. var x = nil
B. var x interface{} = nil
C. var x string = nil
D. var x error = nil

参考答案:BD

可以赋值成nil的变量类型有7种:1)任意类型的指针变量,2)函数变量,3)接口,4)error,5)map, 6)切片 7)通道。这些变量默认值就是nil,实际上都不用赋值成nil。

  1. package main
  2. import "fmt"
  3. func main() {
  4. var ptrI *int
  5. var f func()
  6. var iface interface{}
  7. var err error
  8. var m map[string]string
  9. var sl []string
  10. var ch chan int
  11. if ptrI == nil {fmt.Printf("1) pointer is nil\n")}
  12. if f == nil {fmt.Printf("2) func is nil\n")}
  13. if iface == nil {fmt.Printf("3) interface is nil\n")}
  14. if err == nil {fmt.Printf("4) error is nil\n")}
  15. if m == nil {fmt.Printf("5) map is nil\n")}
  16. if sl == nil {fmt.Printf("6) slice is nil\n")}
  17. if ch == nil {fmt.Printf("7) ch is nil\n")}
  18. }

输出: 

  1. 1) pointer is nil
  2. 2) func is nil
  3. 3) interface is nil
  4. 4) error is nil
  5. 5) map is nil
  6. 6) slice is nil
  7. 7) ch is nil

 不能赋值成nil的变量类型,会在编译时报错:

  1. package main
  2. import "fmt"
  3. type Person struct {
  4. name string
  5. }
  6. func main() {
  7. var str string = nil
  8. var person Person = nil
  9. var i int = nil
  10. var b bool = nil
  11. var x = nil
  12. fmt.Printf("str=%v person=%v i=%v b=%v x=%v", str, person, i, b, x)
  13. }

 编译报错:

  1. # command-line-arguments
  2. ./tryinit.go:10:6: cannot use nil as type string in assignment
  3. ./tryinit.go:11:6: cannot use nil as type Person in assignment
  4. ./tryinit.go:12:6: cannot use nil as type int in assignment
  5. ./tryinit.go:13:6: use of untyped nil

38. 【中级】从切片中删除一个元素,下面的算法实现正确的是()
A.
func (s *Slice)Remove(value interface{})error {
    for i, v := range *s {
       if isEqual(value, v) {
           if i== len(*s) - 1 {
               *s = (*s)[:i]
           }else {
               *s = append((*s)[:i],(*s)[i + 2:]...)
           }
           return nil
       }
    }
    return ERR_ELEM_NT_EXIST
}

B.
func (s*Slice)Remove(value interface{}) error {
    for i, v:= range *s {
        if isEqual(value, v) {
            *s =append((*s)[:i],(*s)[i + 1:])
            return nil
        }
    }
    returnERR_ELEM_NT_EXIST
}

C.
func (s*Slice)Remove(value interface{}) error {
     for i, v:= range *s {
         if isEqual(value, v) {
             delete(*s, v)
             return nil
         }
     }
     returnERR_ELEM_NT_EXIST
}

D.
func (s*Slice)Remove(value interface{}) error {
    for i, v:= range *s {
        if isEqual(value, v) {
            *s =append((*s)[:i],(*s)[i + 1:]...)
            return nil
        }
    }
    returnERR_ELEM_NT_EXIST
}

参考答案:D
A,切片截取是前闭后开;B,append追加另一个切片时需要加"...";C,delete是map的删除方式


51. 【初级】对于局部变量整型切片x的赋值,下面定义正确的是()

A. x := []int{
                 1, 2, 3,
                 4, 5, 6,
                 }
B. x :=[]int{
           1, 2, 3,
           4, 5, 6
          }
C. x :=[]int{
                1, 2, 3,
                4, 5, 6}
D. x :=[]int{1, 2, 3, 4, 5, 6,}

参考答案:ACD

切片赋值时,最后一个元素后面如果有逗号则“}”可以换行或者不换行,否则”}“必须和最后一个元素保持在同一行。


55. 【初级】关于变量的自增和自减操作,下面语句正确的是()

A. i := 1;i++
B. i := 1;j = i++
C. i := 1;++i
D. i := 1;i--

参考答案:AD

Go目前只支持后置自增自减运算符,而且不能用于赋值或者作为参数传递给函数。

  1. package main
  2. import "fmt"
  3. func main() {
  4. i:=1;i--
  5. j := i++ //错误
  6. if i++ > 0 { //错误
  7. fmt.Printf("i=%v\n", i++) //错误
  8. }
  9. ++i //错误
  10. fmt.Printf("i=%v j=%v\n", i, j)
  11. }

编译报错:

  1. # command-line-arguments
  2. ./example.go:7:8: syntax error: unexpected ++ at end of statement
  3. ./example.go:8:9: syntax error: unexpected >, expecting {
  4. ./example.go:9:25: syntax error: unexpected ++, expecting comma or )
  5. ./example.go:11:2: syntax error: unexpected ++, expecting }

57. 【中级】关于函数声明,下面语法错误的是()
A. func f(a, b int) (value int, err error)
B. func f(a int, b int) (value int, err error)
C. func f(a, b int) (value int, error)
D. func f(a int, b int) (int, int, error)

参考答案:C
Go语言的函数返回值,要么全是非命名的返回值,要么全是命名的返回值,不能即有非命名的又有命名的返回值,否则编译会报错:

syntax error: mixed named and unnamed function parameters

58. 【中级】如果Add函数的调用代码为:

func main() {
var a Integer = 1
var b Integer = 2
var i interface{} = &a
sum := i.(*Integer).Add(b)
fmt.Println(sum)
}
则Add函数定义正确的是()
A.
type Integer int
func (a Integer) Add(b Integer) Integer {
 return a + b
}
B
type Integer int
func (a Integer) Add(b *Integer) Integer {
 return a + *b
}
C
type Integer int
func (a *Integer) Add(b Integer) Integer {
 return *a + b
}
D
type Integer int
func (a *Integer) Add(b *Integer) Integer {
 return *a + *b
}
参考答案:AC
对于方法的参数而言,指针类型的参数不能接受值类型的参数,反之亦然,所以BD是错的。而对于结构体的方法而言,不论定义成值方法或者指针方法,都可以同时支持用指针或者非指针实例来调用。(可能是在编译期完成的转换,后续还需要确认)有两种特殊情况只能定义成值方法:

一是接口断言成非指针实例,例如把main函数改为:

  1. func main() {
  2. var a Integer = 1
  3. var b Integer = 2
  4. var c interface{} = a
  5. sum := c.(Integer).Add(b)
  6. fmt.Println(sum)
  7. }

则答案是 A
运行C则编译报错:

  1. ./example.go:9:20: cannot call pointer method on c.(Integer)
  2. ./example.go:9:20: cannot take the address of c.(Integer)

二是类型强制转换,例如把main函数改为:

  1. func main() {
  2. var a int = 1
  3. var b Integer = 2
  4. sum := Integer(a).Add(b)
  5. fmt.Println(sum)
  6. }

则答案是 A
运行C则同样是编译报错:

  1. ./example.go:8:19: cannot call pointer method on Integer(a)
  2. ./example.go:8:19: cannot take the address of Integer(a)

72. 【中级】关于GetPodAction定义,下面赋值正确的是()
type Fragment interface {
    Exec(transInfo *TransInfo) error
}
type GetPodAction struct {
}
func (g GetPodAction) Exec(transInfo *TransInfo) error {
    return nil
}
A. var fragment Fragment =new(GetPodAction)
B. var fragment Fragment = GetPodAction
C. var fragment Fragment = &GetPodAction{}
D. var fragment Fragment = GetPodAction{}
参考答案:ACD
Fragment是接口所以赋值成指针或者非指针,AC是指针,D是非指针。B编译报错“type GetPodAction is not an expression”
结构体创建实例有两种等价的形式:
1) var g GetPodAction; 2) g :=  GetPodAction{}


82. 【中级】关于接口,下面说法正确的是()
A. 只要两个接口拥有相同的方法列表(次序不同不要紧),那么它们就是等价的,可以相互赋值
B. 如果接口A的方法列表是接口B的方法列表的子集,那么接口B可以赋值给接口A
C. 接口查询是否成功,要在运行期才能够确定
D. 接口赋值是否可行,要在运行期才能够确定

参考答案:ABC
接口赋值时编译期就可以报错。
验证代码:

  1. package main
  2. import "fmt"
  3. type A interface {
  4. f1()
  5. f2()
  6. f3()
  7. }
  8. type B interface {
  9. f2()
  10. f1()
  11. }
  12. type AImpl struct {
  13. }
  14. func (a AImpl) f1() {
  15. fmt.Printf("call AImpl f1()\n")
  16. }
  17. func (a AImpl) f2() {
  18. fmt.Printf("call AImpl f2()\n")
  19. }
  20. func (a AImpl) f3() {
  21. fmt.Printf("call AImpl f3()\n")
  22. }
  23. func main() {
  24. var a A = AImpl{}
  25. var b B = a
  26. b.f1()
  27. //接口查询,判断b是否实现了接口A
  28. if c, ok := b.(A); ok {
  29. c.f3()
  30. }
  31. }

输出:

  1. call AImpl f1()
  2. call AImpl f3()

83. 【初级】关于channel,下面语法正确的是()
A. var ch chan int
B. ch := make(chan int)
C. <- ch
D. ch <-

参考答案:ABC


85. 【中级】 golang中大多数数据类型都可以转化为有效的JSON文本,下面几种类型除外()
A. 指针
B. channel
C. complex
D. 函数

参考答案:BCD


87. 【初级】 flag是bool型变量,下面if表达式符合编码规范的是()
A. if flag == 1
B. if flag
C. if flag == false
D. if !flag

参考答案:BD
这道题C也是符合规范的,Go的源代码有很多这样的写法,例如:runtime/pprof/internal/profile:

  1. func encodeBoolOpt(b *buffer, tag int, x bool) {
  2. if x == false {
  3. return
  4. }
  5. encodeBool(b, tag, x)
  6. }

88. 【初级】 value是整型变量,下面if表达式符合编码规范的是()
A. if value == 0
B. if value
C. if value != 0
D. if !value

参考答案:AC
BD两项只能是bool变量,否则编译报错:“non-bool value (type int) used as if condition”,“invalid operation: ! int”


91. 【中级】关于slice或map操作,下面正确的是()
A.
var s []int
s = append(s,1)
B.
var m map[string]int
m["one"] = 1
C.
var s []int
s = make([]int, 0)
s = append(s,1)

D.
var m map[string]int
m = make(map[string]int)
m["one"] = 1

参考答案:ACD
slice和map在插入数据之前都需要分配内存,而append函数内置有内存分配操作,所以在调用append之前可以不用make切片。
 


93. 【中级】关于channel的特性,下面说法正确的是()
A. 给一个 nil channel 发送数据,造成永远阻塞
B. 从一个 nil channel 接收数据,造成永远阻塞
C. 给一个已经关闭的 channel 发送数据,引起 panic
D. 从一个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回一个零值

参考答案:ABCD
这道题AB两个选项应该是适用于Go的早期版本,在Go1.13.8里面,AB直接报fatal error然后退出。C,D是正确的。
下面是一段能正常运行的代码:

  1. func main() {
  2. ch := make(chan bool)
  3. go func(ch chan bool) {
  4. time.Sleep(1 * time.Second)
  5. b := <-ch
  6. fmt.Printf("b=%v\n", b)
  7. }(ch)
  8. ch <- true
  9. fmt.Printf("exit\n")
  10. }
  1. b=true
  2. exit

按照AB说法,把"ch := make(chan bool)"改成:

  1. func main() {
  2. var ch chan bool
  3. go func(ch chan bool) {
  4. time.Sleep(1 * time.Second)
  5. b := <-ch
  6. fmt.Printf("b=%v\n", b)
  7. }(ch)
  8. ch <- true
  9. fmt.Printf("exit\n")
  10. }

报错退出:

  1. fatal error: all goroutines are asleep - deadlock!
  2. goroutine 1 [chan send (nil chan)]:
  3. main.main()
  4. /home/zhoupeng/go-test/src/tutorial/example.go:15 +0x5b
  5. goroutine 6 [chan receive (nil chan)]:
  6. main.main.func1(0x0)
  7. /home/zhoupeng/go-test/src/tutorial/example.go:12 +0x4b
  8. created by main.main
  9. /home/zhoupeng/go-test/src/tutorial/example.go:10 +0x42
  10. exit status 2

验证C,在“ch <- true”之前加入“close(ch)”,则报错:“panic: send on closed channel”

  1. func main() {
  2. ch := make(chan bool)
  3. go func(ch chan bool) {
  4. time.Sleep(1 * time.Second)
  5. b := <-ch
  6. fmt.Printf("b=%v\n", b)
  7. }(ch)
  8. close(ch)
  9. ch <- true
  10. fmt.Printf("exit\n")
  11. }

验证D,通道写入true之后关闭通道,等待3秒,协程等待1秒后连读2次通道,第一次读到缓冲区里的“true”,第二次缓冲区为空则读取到布尔变量的零值false。注,数值型变量的零值是0,字符串的零值是“”。

  1. func main() {
  2. ch := make(chan bool, 1)
  3. go func(ch chan bool) {
  4. time.Sleep(1 * time.Second)
  5. b1 := <-ch
  6. fmt.Printf("b1=%v\n", b1)
  7. b2 := <-ch
  8. fmt.Printf("b2=%v\n", b2)
  9. }(ch)
  10. ch <- true
  11. close(ch)
  12. time.Sleep(3 * time.Second)
  13. fmt.Printf("exit\n")
  14. }
  1. b1=true
  2. b2=false
  3. exit

94. 【中级】关于无缓冲和有冲突的channel,下面说法正确的是()
A. 无缓冲的channel是默认的缓冲为1的channel
B. 无缓冲的channel和有缓冲的channel都是同步的
C. 无缓冲的channel和有缓冲的channel都是非同步的
D. 无缓冲的channel是同步的,而有缓冲的channel是非同步的

参考答案:D
无缓冲的channel是默认的缓冲为0的channel,对应代码如“make(chan int, 0)”,0是默认值可以省略。有缓冲的channel是非同步的指的是发送/接收操作只在缓冲区满了才阻塞,否则继续执行后面的代码。
缓存为1的时序验证:

  1. func main() {
  2. ch := make(chan int, 1)
  3. go func(ch chan int) {
  4. time.Sleep(2 * time.Second)
  5. i1 := <-ch
  6. log.Printf("i1=%v\n", i1)
  7. time.Sleep(2 * time.Second)
  8. i2 := <-ch
  9. log.Printf("i2=%v\n", i2)
  10. }(ch)
  11. log.Printf("send 1\n")
  12. ch <- 1
  13. log.Printf("send 2\n")
  14. ch <- 2
  15. log.Printf("main sleep\n")
  16. time.Sleep(3 * time.Second)
  17. log.Printf("exit\n")
  18. }
  1. 2020/07/13 14:19:26 send 1
  2. 2020/07/13 14:19:26 send 2
  3. 2020/07/13 14:19:28 i1=1
  4. 2020/07/13 14:19:28 main sleep
  5. 2020/07/13 14:19:30 i2=2
  6. 2020/07/13 14:19:31 exit

从输出可以看出,缓冲区是一个先进先出的队列,发送的顺序是1,2,接收的顺序也是1,2。运行到send2时缓冲区已满,main函数所在协程阻塞,2秒之后另一个协程从通道读取到第一个值1,缓冲区不再是已满的状态,main函数停止阻塞继续执行后面的代码打印“main sleep”,再过2秒之后另一个协程从通道读取到第二个值2。
如果把上述代码改为非缓冲通道,则输出时序为:

  1. 2020/07/13 14:37:15 send 1
  2. 2020/07/13 14:37:17 i1=1
  3. 2020/07/13 14:37:17 send 2
  4. 2020/07/13 14:37:19 i2=2
  5. 2020/07/13 14:37:19 main sleep
  6. 2020/07/13 14:37:22 exit

send1之后就会阻塞,2秒之后等另一个协程读取到第一个值1之后,main再发送send 2,发了之后再阻塞 ,2秒之后等另一个协程读取到第二个值2之后,main解除阻塞。


95. 【中级】关于异常的触发,下面说法正确的是()
A. 空指针解析
B. 下标越界
C. 除数为0
D. 调用panic函数

参考答案:ABCD


96. 【中级】关于cap函数的适用类型,下面说法正确的是()
A. array
B. slice
C. map
D. channel

参考答案:ABD

  1. func main(){
  2. var a [3]int
  3. var sl = make([]int, 1, 5)
  4. var m = make(map[int]int, 100)
  5. var ch = make(chan int, 10)
  6. log.Printf("%d\n",cap(a))
  7. log.Printf("%d\n",cap(sl))
  8. log.Printf("%d\n",cap(m))
  9. log.Printf("%d\n",cap(ch))
  10. }

编译报错:

invalid argument m (type map[int]int) for cap


100.             【中级】关于map,下面说法正确的是()
A. map反序列化时json.unmarshal的入参必须为map的地址
B. 在函数调用中传递map,则子函数中对map元素的增加不会导致父函数中map的修改
C. 在函数调用中传递map,则子函数中对map元素的修改不会导致父函数中map的修改
D. 不能使用内置函数delete删除map的元素

参考答案:A
A:func Unmarshal(data []byte, v interface{}) error 其中“If v is nil or not a pointer, Unmarshal returns an InvalidUnmarshalError.”所以v只能是非空指针。
B,C:子函数对map的"增,删,改"都会导致父函数中map的改变,验证代码如下:

  1. func test(mp map[int]int){
  2. mp[23] = 321
  3. mp[11] = 100
  4. delete(mp, 12)
  5. }
  6. func main(){
  7. var m = make(map[int]int)
  8. m[11] = 108
  9. m[12] = 110
  10. log.Printf("%v\n",m)
  11. test(m)
  12. log.Printf("%v\n",m)
  13. }
  1. [root@dev tutorial]# go run example.go
  2. 2020/07/14 10:18:49 map[11:108 12:110]
  3. 2020/07/14 10:18:49 map[11:100 23:321]

102. 【初级】关于select机制,下面说法正确的是()
A. select机制用来处理异步IO问题
B. select机制最大的一条限制就是每个case语句里必须是一个IO操作
C. golang在语言级别支持select关键字
D. select关键字的用法与switch语句非常类似,后面要带判断条件

参考答案:ABC
A 不准确,可以用来处理异步IO问题,也可以处理同步IO; B不对,每个case语句里必须是接收通道的操作,不一定非得是IO操作,golang并没有这样的硬性限制。C. 是对的,golang ”语言级别“ 支持select关键字,而不需要引用其它包。D.后面跟的是对通道的接收操作。


103.             【初级】关于内存泄露,下面说法正确的是()
A. golang有自动垃圾回收,不存在内存泄露
B. golang中检测内存泄露主要依靠的是pprof包
C. 内存泄露可以在编译阶段发现
D. 应定期使用浏览器来查看系统的实时内存信息,及时发现内存泄露问题

参考答案:BD

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/445659
推荐阅读
相关标签
  

闽ICP备14008679号