Golang精編100題-搞定golang面試

尹成發表於2018-05-26

Golang精編100

能力模型

級別

模型

初級
primary

熟悉基本語法,能夠看懂程式碼的意圖;
在他人指導下能夠完成使用者故事的開發,編寫的程式碼符合CleanCode規範;

中級
intermediate

能夠獨立完成使用者故事的開發和測試;
能夠嗅出程式碼的壞味道,並知道如何重構達成目標;

高階
senior

能夠開發出高質量高效能的程式碼;
能夠熟練使用高階特性,開發程式設計框架或測試框架;

選擇題

1.   【初級】下面屬於關鍵字的是()
A. func
B. def
C. struct
D. class

參考答案:AC

 

2.   【初級】定義一個包內全域性字串變數,下面語法正確的是()
A. var str string
B. str := ""
C. str = ""
D. var str = ""

參考答案:AD

 

3.   【初級】通過指標變數 p 訪問其成員變數 name,下面語法正確的是()
A. p.name
B. (*p).name
C. (&p).name
D. p->name

參考答案:AB

 

4.   【初級】關於介面和類的說法,下面說法正確的是()
A.
一個類只需要實現了介面要求的所有函式,我們就說這個類實現了該介面
B.
實現類的時候,只需要關心自己應該提供哪些方法,不用再糾結介面需要拆得多細才合理
C.
類實現介面時,需要匯入介面所在的包
D.
介面由使用方按自身需求來定義,使用方無需關心是否有其他模組定義過類似的介面

參考答案:ABD

 

5.   【初級】關於字串連線,下面語法正確的是()
A. str := ‘abc’ + ‘123’
B. str := "abc" + "123"
C. str
= '123' + "abc"
D. fmt.Sprintf("abc%d", 123)

參考答案:BD

 

6.   【初級】關於協程,下面說法正確是()
A.
協程和執行緒都可以實現程式的併發執行
B.
執行緒比協程更輕量級
C.
協程不存在死鎖問題
D.
通過channel來進行協程間的通訊

參考答案:AD

 

7.   【中級】關於init函式,下面說法正確的是()
A.
一個包中,可以包含多個init函式
B.
程式編譯時,先執行匯入包的init函式,再執行本包內的init函式
C. main
包中,不能有init函式
D. init
函式可以被其他函式呼叫

參考答案:AB

 

8.   【初級】關於迴圈語句,下面說法正確的有()
A.
迴圈語句既支援for關鍵字,也支援whiledo-while
B.
關鍵字for的基本使用方法與C/C++中沒有任何差異
C. for
迴圈支援continuebreak來控制迴圈,但是它提供了一個更高階的break,可以選擇中斷哪一個迴圈
D. for
迴圈不支援以逗號為間隔的多個賦值語句,必須使用平行賦值的方式來初始化多個變數 
參考答案:CD

 

9.   【中級】對於函式定義:

10. func add(args ...int) int {

11.  sum :=0

12.  for _,arg := range args {

13.     sum += arg

14.  }

15.  returnsum

}

下面對add函式呼叫正確的是()
A. add(1, 2)
B. add(1, 3, 7)
C. add([]int{1, 2})
D. add([]int{1, 3, 7}...)

參考答案:ABD

 

16. 【初級】關於型別轉化,下面語法正確的是()
A.

17. type MyInt int

18. var i int = 1

var jMyInt = i

B.

type MyIntint

var i int= 1

var jMyInt = (MyInt)i

C.

type MyIntint

var i int= 1

var jMyInt = MyInt(i)

D.

type MyIntint

var i int= 1

var jMyInt = i.(MyInt)

參考答案:C

 

19. 【初級】關於區域性變數的初始化,下面正確的使用方式是()
A. var i int = 10
B. var i = 10
C. i := 10
D. i = 10

參考答案:ABC

 

20. 【初級】關於const常量定義,下面正確的使用方式是()
A.

21. const Pi float64 = 3.14159265358979323846

const zero= 0.0

B.

const (

size int64= 1024

eof = -1

)

C.

const (

ERR_ELEM_EXISTerror = errors.New("element already exists")

ERR_ELEM_NT_EXISTerror = errors.New("element not exists")

)

D.

const u, vfloat32 = 0, 3

const a,b, c = 3, 4, "foo"

參考答案:ABD

 

22. 【初級】關於布林變數b的賦值,下面錯誤的用法是()
A. b = true
B. b = 1
C. b = bool(1)
D. b = (1 == 2)

參考答案:BC

 

23. 【中級】下面的程式的執行結果是()

24. func main() {  

25. if (true) {

26.    defer fmt.Printf("1")

27. } else {

28.    defer fmt.Printf("2")

29. }

30. fmt.Printf("3")

}

A. 321
B. 32
C. 31
D. 13

參考答案:C

 

31. 【初級】關於switch語句,下面說法正確的有()
A.
條件表示式必須為常量或者整數
B.
單個case中,可以出現多個結果選項
C.
需要用break來明確退出一個case
D.
只有在case中明確新增fallthrough關鍵字,才會繼續執行緊跟的下一個case

參考答案:BD

 

32. 【中級】 golang中沒有隱藏的this指標,這句話的含義是()
A.
方法施加的物件顯式傳遞,沒有被隱藏起來
B. golang
沿襲了傳統物件導向程式設計中的諸多概念,比如繼承、虛擬函式和建構函式
C. golang
的物件導向表達更直觀,對於程式導向只是換了一種語法形式來表達
D.
方法施加的物件不需要非得是指標,也不用非得叫this

參考答案:ACD

 

33. 【中級】 golang中的引用型別包括()
A.
陣列切片
B. map
C. channel
D. interface

參考答案:ABCD

 

34. 【中級】 golang中的指標運算包括()
A.
可以對指標進行自增或自減運算
B.
可以通過“&”取指標的地址
C.
可以通過“*”取指標指向的資料
D.
可以對指標進行下標運算

參考答案:BC

 

35. 【初級】關於main函式(可執行程式的執行起點),下面說法正確的是()
A. main
函式不能帶引數
B. main
函式不能定義返回值
C. main
函式所在的包必須為main
D. main
函式中可以使用flag包來獲取和解析命令列引數

參考答案:ABCD

 

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

參考答案:BD

 

37. 【中級】關於整型切片的初始化,下面正確的是()
A. s := make([]int)
B. s := make([]int, 0)
C. s := make([]int, 5, 10)
D. s := []int{1, 2, 3, 4, 5}

參考答案:BCD

 

38. 【中級】從切片中刪除一個元素,下面的演算法實現正確的是()
A.

39. func (s *Slice)Remove(value interface{})error {

40. for i, v := range *s {

41.    if isEqual(value, v) {

42.        if i== len(*s) - 1 {

43.            *s = (*s)[:i]

44.        }else {

45.            *s = append((*s)[:i],(*s)[i + 2:]...)

46.        }

47.        return nil

48.    }

49. }

50. 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

 

51. 【初級】對於區域性變數整型切片x的賦值,下面定義正確的是()
A.

52. x := []int{

53. 1, 2, 3,

54. 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.

56. i := 1

i++

B.

i := 1

j = i++

C.

i := 1

++i

D.

i := 1

i--

參考答案:AD

 

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

 

58. 【中級】如果Add函式的呼叫程式碼為:

59. func main() {

60. var a Integer = 1

61. var b Integer = 2

62. var i interface{} = &a

63. sum := i.(*Integer).Add(b)

64. fmt.Println(sum)

}

Add函式定義正確的是()
A.

typeInteger int

func (aInteger) Add(b Integer) Integer {

 return a + b

}

B.

typeInteger int

func (aInteger) Add(b *Integer) Integer {

 return a + *b

}

C.

typeInteger int

func (a*Integer) Add(b Integer) Integer {

 return *a + b

}

D.

typeInteger int

func (a*Integer) Add(b *Integer) Integer {

 return *a + *b

}

參考答案:AC

 

65. 【中級】如果Add函式的呼叫程式碼為:

66. func main() {

67. var a Integer = 1

68. var b Integer = 2

69. var i interface{} = a

70. sum := i.(Integer).Add(b)

71. fmt.Println(sum)

}

Add函式定義正確的是()
A.

typeInteger int

func (a Integer)Add(b Integer) Integer {

 return a + b

}

B.

typeInteger int

func (aInteger) Add(b *Integer) Integer {

 return a + *b

}

C.

typeInteger int

func (a*Integer) Add(b Integer) Integer {

 return *a + b

}

D.

typeInteger int

func (a*Integer) Add(b *Integer) Integer {

 return *a + *b

}

參考答案:A

 

72. 【中級】關於GetPodAction定義,下面賦值正確的是()

73. type Fragment interface {

74. Exec(transInfo *TransInfo) error

75. }

76. type GetPodAction struct {

77. }

78. func (g GetPodAction) Exec(transInfo*TransInfo) error {

79. ...

80. return nil

}

A. var fragment Fragment =new(GetPodAction)
B. var fragment Fragment = GetPodAction
C. var fragment Fragment = &GetPodAction{}
D. var fragment Fragment = GetPodAction{}

參考答案:ACD

 

81. 【中級】關於GoMock,下面說法正確的是()
A. GoMock
可以對interface打樁
B. GoMock
可以對類的成員函式打樁
C. GoMock
可以對函式打樁
D. GoMock
打樁後的依賴注入可以通過GoStub完成

參考答案:AD

 

82. 【中級】關於介面,下面說法正確的是()
A.
只要兩個介面擁有相同的方法列表(次序不同不要緊),那麼它們就是等價的,可以相互賦值
B.
如果介面A的方法列表是介面B的方法列表的子集,那麼介面B可以賦值給介面A
C.
介面查詢是否成功,要在執行期才能夠確定
D.
介面賦值是否可行,要在執行期才能夠確定

參考答案:ABC

 

83. 【初級】關於channel,下面語法正確的是()
A. var ch chan int
B. ch := make(chan int)
C. <- ch
D. ch <-

參考答案:ABC

 

84. 【初級】關於同步鎖,下面說法正確的是()
A.
當一個goroutine獲得了Mutex後,其他goroutine就只能乖乖的等待,除非該goroutine釋放這個Mutex
B. RWMutex
在讀鎖佔用的情況下,會阻止寫,但不阻止讀
C. RWMutex
在寫鎖佔用情況下,會阻止任何其他goroutine(無論讀和寫)進來,整個鎖相當於由該goroutine獨佔
D. Lock()
操作需要保證有Unlock()RUnlock()呼叫與之對應

參考答案:ABC

 

85. 【中級】 golang中大多數資料型別都可以轉化為有效的JSON文字,下面幾種型別除外()
A.
指標
B. channel
C. complex
D.
函式

參考答案:BCD

 

86. 【中級】關於go vendor,下面說法正確的是()
A.
基本思路是將引用的外部包的原始碼放在當前工程的vendor目錄下面
B.
編譯go程式碼會優先從vendor目錄先尋找依賴包
C.
可以指定引用某個特定版本的外部包
D.
有了vendor目錄後,打包當前的工程程式碼到其他機器的$GOPATH/src下都可以通過編譯

參考答案:ABD

 

87. 【初級】 flagbool型變數,下面if表示式符合編碼規範的是()
A. if flag == 1
B. if flag
C. if flag == false
D. if !flag

參考答案:BD

 

88. 【初級】 value是整型變數,下面if表示式符合編碼規範的是()
A. if value == 0
B. if value
C. if value != 0
D. if !value

參考答案:AC

 

89. 【中級】關於函式返回值的錯誤設計,下面說法正確的是()
A.
如果失敗原因只有一個,則返回bool
B.
如果失敗原因超過一個,則返回error
C.
如果沒有失敗原因,則不返回boolerror
D.
如果重試幾次可以避免失敗,則不要立即返回boolerror

參考答案:ABCD

 

90. 【中級】關於異常設計,下面說法正確的是()
A.
在程式開發階段,堅持速錯,讓程式異常崩潰
B.
在程式部署後,應恢復異常避免程式終止
C.
一切皆錯誤,不用進行異常設計
D.
對於不應該出現的分支,使用異常處理

參考答案:ABD

 

91. 【中級】關於slicemap操作,下面正確的是()
A.

92. var s []int

s =append(s,1)

B.

var mmap[string]int

m["one"]= 1

C.

var s[]int

s =make([]int, 0)

s =append(s,1)

D.

var mmap[string]int

m =make(map[string]int)

m["one"]= 1

參考答案:ACD

 

93. 【中級】關於channel的特性,下面說法正確的是()
A.
給一個 nil channel 傳送資料,造成永遠阻塞
B.
從一個 nil channel 接收資料,造成永遠阻塞
C.
給一個已經關閉的 channel 傳送資料,引起 panic
D.
從一個已經關閉的 channel 接收資料,如果緩衝區中為空,則返回一個零值

參考答案:ABCD

 

94. 【中級】關於無緩衝和有衝突的channel,下面說法正確的是()
A.
無緩衝的channel是預設的緩衝為1channel
B.
無緩衝的channel和有緩衝的channel都是同步的
C.
無緩衝的channel和有緩衝的channel都是非同步的
D.
無緩衝的channel是同步的,而有緩衝的channel是非同步的

參考答案:D

 

95. 【中級】關於異常的觸發,下面說法正確的是()
A.
空指標解析
B.
下標越界
C.
除數為0
D.
呼叫panic函式

參考答案:ABCD

 

96. 【中級】關於cap函式的適用型別,下面說法正確的是()
A. array
B. slice
C. map
D. channel

參考答案:ABD

 

97. 【中級】關於beego框架,下面說法正確的是()
A. beego
是一個golang實現的輕量級HTTP框架
B. beego
可以通過註釋路由、正則路由等多種方式完成url路由注入
C.
可以使用bee new工具生成空工程,然後使用bee run命令自動熱編譯
D. beego
框架只提供了對url路由的處理,而對於MVC架構中的資料庫部分未提供框架支援

參考答案:ABC

 

98. 【中級】關於goconvey,下面說法正確的是()
A. goconvey
是一個支援golang的單元測試框架
B. goconvey
能夠自動監控檔案修改並啟動測試,並可以將測試結果實時輸出到web介面
C. goconvey
提供了豐富的斷言簡化測試用例的編寫
D. goconvey
無法與go test整合

參考答案:ABC

 

99. 【中級】關於go vet,下面說法正確的是()
A. go vet
golang自帶工具go tool vet的封裝
B.
當執行go vet database時,可以對database所在目錄下的所有子資料夾進行遞迴檢測
C. go vet
可以使用絕對路徑、相對路徑或相對GOPATH的路徑指定待檢測的包
D. go vet
可以檢測出死程式碼

參考答案:ACD

 

100.             【中級】關於map,下面說法正確的是()
A. map
反序列化時json.unmarshal的入參必須為map的地址
B.
在函式呼叫中傳遞map,則子函式中對map元素的增加不會導致父函式中map的修改
C.
在函式呼叫中傳遞map,則子函式中對map元素的修改不會導致父函式中map的修改
D.
不能使用內建函式delete刪除map的元素

參考答案:A

 

101.             【中級】關於GoStub,下面說法正確的是()
A. GoStub
可以對全域性變數打樁
B. GoStub
可以對函式打樁
C. GoStub
可以對類的成員方法打樁
D. GoStub
可以打動態樁,比如對一個函式打樁後,多次呼叫該函式會有不同的行為

參考答案:ABD

 

102.             【初級】關於select機制,下面說法正確的是()
A. select
機制用來處理非同步IO問題
B. select
機制最大的一條限制就是每個case語句裡必須是一個IO操作
C. golang
在語言級別支援select關鍵字
D. select
關鍵字的用法與switch語句非常類似,後面要帶判斷條件

參考答案:ABC

 

103.             【初級】關於記憶體洩露,下面說法正確的是()
A. golang
有自動垃圾回收,不存在記憶體洩露
B. golang
中檢測記憶體洩露主要依靠的是pprof
C.
記憶體洩露可以在編譯階段發現
D.
應定期使用瀏覽器來檢視系統的實時記憶體資訊,及時發現記憶體洩露問題

參考答案:BD

 

填空題

1.   【初級】宣告一個整型變數i__________

參考答案:var i int

 

2.   【初級】宣告一個含有10個元素的整型陣列a__________

參考答案:var a [10]int

 

3.   【初級】宣告一個整型陣列切片s__________

參考答案:var s []int

 

4.   【初級】宣告一個整型指標變數p__________

參考答案:var p *int

 

5.   【初級】宣告一個key為字串型value為整型的map變數m__________

參考答案:var m map[string]int

 

6.   【初級】宣告一個入參和返回值均為整型的函式變數f__________

參考答案:var f func(a int) int

 

7.   【初級】宣告一個只用於讀取int資料的單向channel變數ch__________

參考答案:var ch <-chan int

 

8.   【初級】假設原始檔的命名為slice.go,則測試檔案的命名為__________

參考答案:slice_test.go

 

9.   【初級】 go test要求測試函式的字首必須命名為__________

參考答案:Test

 

10. 【中級】下面的程式的執行結果是__________

11. for i := 0; i < 5; i++ {

12. defer fmt.Printf("%d ", i)

}

參考答案:4 3 2 1 0

 

13. 【中級】下面的程式的執行結果是__________

14. func main() {

15. x := 1

16. {

17.    x := 2

18.    fmt.Print(x)

19. }

20. fmt.Println(x)

}

參考答案:21

 

21. 【中級】下面的程式的執行結果是__________

22. func main() {

23. strs := []string{"one","two", "three"}

24.  

25. for _, s := range strs {

26.    go func() {

27.        time.Sleep(1 * time.Second)

28.        fmt.Printf("%s ", s)

29.    }()

30. }

31. time.Sleep(3 * time.Second)

}

參考答案:three threethree

 

32. 【中級】下面的程式的執行結果是__________

33. func main() {  

34. x := []string{"a", "b","c"}

35. for v := range x {

36.    fmt.Print(v)

37. }

}

參考答案:012

 

38. 【中級】下面的程式的執行結果是__________

39. func main() {  

40. x := []string{"a", "b","c"}

41. for _, v := range x {

42.    fmt.Print(v)

43. }

}

參考答案:abc

 

44. 【初級】下面的程式的執行結果是__________

45. func main() {  

46. i := 1

47. j := 2

48. i, j = j, i

49. fmt.Printf("%d%d\n", i, j)

}

參考答案:21

 

50. 【初級】下面的程式的執行結果是__________

51. func incr(p *int) int {

52. *p++  

53. return *p

54. }

55. func main() {  

56. v := 1

57. incr(&v)

58. fmt.Println(v)

}

參考答案:2

 

59. 【初級】啟動一個goroutine的關鍵字是__________

參考答案:go

 

60. 【中級】下面的程式的執行結果是__________

61. type Slice []int

62. func NewSlice() Slice {

63. return make(Slice, 0)

64. }

65. func (s* Slice) Add(elem int) *Slice {

66. *s = append(*s, elem)

67. fmt.Print(elem)

68. return s

69. }

70. func main() {  

71. s := NewSlice()

72. defer s.Add(1).Add(2)

73. s.Add(3)

}

參考答案:132

 

判斷題

1.   【初級】陣列是一個值型別()

參考答案:T

 

2.   【初級】使用map不需要引入任何庫()

參考答案:T

 

3.   【中級】內建函式delete可以刪除陣列切片內的元素()

參考答案:F

 

4.   【初級】指標是基礎型別()

參考答案:F

 

5.   【初級】 interface{}是可以指向任意物件的Any型別()

參考答案:T

 

6.   【中級】下面關於檔案操作的程式碼可能觸發異常()

7.  file, err := os.Open("test.go")

8.  defer file.Close()

9.  if err != nil {

10.  fmt.Println("open file failed:",err)

11.  return

12. }

...

參考答案:T

 

13. 【初級】 Golang不支援自動垃圾回收()

參考答案:F

 

14. 【初級】 Golang支援反射,反射最常見的使用場景是做物件的序列化()

參考答案:T

 

15. 【初級】 Golang可以複用C/C++的模組,這個功能叫Cgo()

參考答案:F

 

16. 【初級】下面程式碼中兩個斜點之間的程式碼,比如json:"x",作用是X欄位在從結構體例項編碼到JSON資料格式的時候,使用x作為名字,這可以看作是一種重新命名的方式()

17. type Position struct {

18. X int `json:"x"`

19. Y int `json:"y"`

20. Z int `json:"z"`

}

參考答案:T

 

21. 【初級】通過成員變數或函式首字母的大小寫來決定其作用域()

參考答案:T

 

22. 【初級】對於常量定義zero(const zero = 0.0)zero是浮點型常量()

參考答案:F

 

23. 【初級】對變數x的取反操作是~x()

參考答案:F

 

24. 【初級】下面的程式的執行結果是xello()

25. func main() {

26. str := "hello"

27. str[0] = 'x'

28. fmt.Println(str)

}

參考答案:F

 

29. 【初級】 golang支援goto語句()

參考答案:T

 

30. 【初級】下面程式碼中的指標p為野指標,因為返回的棧記憶體在函式結束時會被釋放()

31. type TimesMatcher struct {

32. base int

33. }

34. func NewTimesMatcher(base int) *TimesMatcher{

35. return &TimesMatcher{base:base}

36. }

37. func main() {

38. p := NewTimesMatcher(3)

39. ...

}

參考答案:F

 

40. 【初級】匿名函式可以直接賦值給一個變數或者直接執行()

參考答案:T

 

41. 【初級】如果呼叫方呼叫了一個具有多返回值的方法,但是卻不想關心其中的某個返回值,可以簡單地用一個下劃線“_”來跳過這個返回值,該下劃線對應的變數叫匿名變數()

參考答案:T

 

42. 【初級】在函式的多返回值中,如果有errorbool型別,則一般放在最後一個()

參考答案:T

 

43. 【初級】錯誤是業務過程的一部分,而異常不是()

參考答案:T

 

44. 【初級】函式執行時,如果由於panic導致了異常,則延遲函式不會執行()

參考答案:F

 

45. 【中級】當程式執行時,如果遇到引用空指標、下標越界或顯式呼叫panic函式等情況,則先觸發panic函式的執行,然後呼叫延遲函式。呼叫者繼續傳遞panic,因此該過程一直在呼叫棧中重複發生:函式停止執行,呼叫延遲執行函式。如果一路在延遲函式中沒有recover函式的呼叫,則會到達該攜程的起點,該攜程結束,然後終止其他所有攜程,其他攜程的終止過程也是重複發生:函式停止執行,呼叫延遲執行函式()

參考答案:F

 

46. 【初級】同級檔案的包名不允許有多個()

參考答案:T

 

47. 【中級】可以給任意型別新增相應的方法()

參考答案:F

 

48. 【初級】 golang雖然沒有顯式的提供繼承語法,但是通過匿名組合實現了繼承()

參考答案:T

 

49. 【初級】使用for range迭代map時每次迭代的順序可能不一樣,因為map的迭代是隨機的()

參考答案:T

 

50. 【初級】 switch後面可以不跟表示式()

參考答案:T

 

51. 【中級】結構體在序列化時非匯出變數(以小寫字母開頭的變數名)不會被encode,因此在decode時這些非匯出變數的值為其型別的零值()

參考答案:T

 

52. 【初級】 golang中沒有建構函式的概念,物件的建立通常交由一個全域性的建立函式來完成,以NewXXX來命名()

參考答案:T

 

53. 【中級】當函式deferDemo返回失敗時,並不能destroycreate成功的資源()

54. func deferDemo() error {

55. err := createResource1()

56. if err != nil {

57.    return ERR_CREATE_RESOURCE1_FAILED

58. }

59. defer func() {

60.    if err != nil {

61.        destroyResource1()

62.    }

63. }()

64.  

65. err = createResource2()

66. if err != nil {

67.    return ERR_CREATE_RESOURCE2_FAILED

68. }

69. defer func() {

70.    if err != nil {

71.        destroyResource2()

72.    }

73. }()

74.  

75. err = createResource3()

76. if err != nil {

77.    return ERR_CREATE_RESOURCE3_FAILED

78. }

79. return nil

}

參考答案:F

 

80. 【中級】 channel本身必然是同時支援讀寫的,所以不存在單向channel()

參考答案:F

 

81. 【初級】 import後面的最後一個元素是包名()

參考答案:F

 

 

 

 

 

 

 

 

 

 

 

 

最近在很多地方看到了golang的面試題,看到了很多人對Golang的面試題心存恐懼,也是為了複習基礎,我把解題的過程總結下來。

面試題

1 寫出下面程式碼輸出內容。

package main
import (   
"fmt"
)
funcmain() {
 
   defer_call()
}
funcdefer_call() {
 
   deferfunc() {fmt.Println("列印前")}()
 
   deferfunc() {fmt.Println("列印中")}()
 
   deferfunc() {fmt.Println("列印後")}()
 
   panic("觸發異常")
}

考點:defer執行順序
解答:
defer 是後進先出。
panic 需要等defer 結束後才會向上傳遞。 出現panic恐慌時候,會先按照defer的後入先出的順序執行,最後才會執行panic。

列印後
列印中
列印前
panic: 觸發異常

2 以下程式碼有什麼問題,說明原因。

type student struct {
 
   Name string
 
   Age  int
}
funcpase_student() {
 
   m := make(map[string]*student)
 
   stus := []student{
        {Name: "zhou",Age: 24},
 
       {Name: "li",Age: 23},
 
       {Name: "wang",Age: 22},
 
   }    for _,stu := range stus {
 
       m[stu.Name] =&stu
    }
}

考點:foreach
解答:
這樣的寫法初學者經常會遇到的,很危險! 與Java的foreach一樣,都是使用副本的方式。所以m[stu.Name]=&stu實際上一致指向同一個指標, 最終該指標的值為遍歷的最後一個struct的值拷貝。 就像想修改切片元素的屬性:

for _, stu := rangestus {
 
   stu.Age = stu.Age+10}

也是不可行的。 大家可以試試列印出來:

func pase_student() {
 
   m := make(map[string]*student)
 
   stus := []student{
        {Name: "zhou",Age: 24},
 
       {Name: "li",Age: 23},
 
       {Name: "wang",Age: 22},
 
   }    
    // 錯誤寫法
 
   for _,stu := range stus {
 
       m[stu.Name] =&stu
    }    
     fork,v:=range m{        
 
     println(k,"=>",v.Name)
 
   }    
      // 正確
 
   for i:=0;i<len(stus);i++ {
 
      m[stus[i].Name] = &stus[i]
    }    
     fork,v:=range m{        
 
      println(k,"=>",v.Name)
 
   }
}

3 下面的程式碼會輸出什麼,並說明原因

func main() {
 
   runtime.GOMAXPROCS(1)
 
   wg := sync.WaitGroup{}
    wg.Add(20)   for i := 0; i < 10; i++ {        
 
        gofunc() {
 
          fmt.Println("A: ", i)
 
          wg.Done()
        }()
    }    
        for i:= 0; i < 10; i++ {        
 
          gofunc(i int) {
 
          fmt.Println("B: ", i)
 
          wg.Done()
        }(i)
    }
    wg.Wait()
}

考點:go執行的隨機性和閉包
解答:
誰也不知道執行後列印的順序是什麼樣的,所以只能說是隨機數字。 但是A:均為輸出10,B:從0~9輸出(順序不定)。 第一個go func中i是外部for的一個變數,地址不變化。遍歷完成後,最終i=10。 故go func執行時,i的值始終是10。

第二個go func中i是函式引數,與外部for中的i完全是兩個變數。 尾部(i)將發生值拷貝,go func內部指向值拷貝地址。
4 下面程式碼會輸出什麼?

type People struct{}func (p *People)ShowA() {
 
   fmt.Println("showA")
 
   p.ShowB()
}
func(p*People)ShowB() {
 
   fmt.Println("showB")
}
typeTeacher struct {
 
   People
}
func(t*Teacher)ShowB() {
 
   fmt.Println("teachershowB")
}
funcmain() {
 
   t := Teacher{}
    t.ShowA()
}

考點:go的組合繼承
解答:
這是Golang的組合模式,可以實現OOP的繼承。 被組合的型別People所包含的方法雖然升級成了外部型別Teacher這個組合型別的方法(一定要是匿名欄位),但它們的方法(ShowA())呼叫時接受者並沒有發生變化。 此時People型別並不知道自己會被什麼型別組合,當然也就無法呼叫方法時去使用未知的組合者Teacher型別的功能。
showAshowB

5 下面程式碼會觸發異常嗎?請詳細說明

func main() {
 
   runtime.GOMAXPROCS(1)
 
   int_chan := make(chanint, 1)
 
   string_chan := make(chanstring, 1)
 
   int_chan <- 1
 
   string_chan <- "hello"
 
   select {   
 
           case value := <-int_chan:
 
      fmt.Println(value)
          casevalue := <-string_chan:        
 
         panic(value)
 
   }
}

考點:select隨機性
解答:
select會隨機選擇一個可用通用做收發操作。 所以程式碼是有肯觸發異常,也有可能不會。 單個chan如果無緩衝時,將會阻塞。但結合 select可以在多個chan間等待執行。有三點原則:

select 中只要有一個case能return,則立刻執行。
當如果同一時間有多個case均能return則偽隨機方式抽取任意一個執行。
如果沒有一個case能return則可以執行”default”塊。

6 下面程式碼輸出什麼?

funccalc(indexstring, a, bint) int {
 
   ret := a+ b
 
   fmt.Println(index,a, b, ret)
 
   return ret
}
funcmain() {   
 
     a := 1
 
   b := 2
 
   defer calc("1", a,calc("10", a, b))    a = 0
 
   defer calc("2", a,calc("20", a, b))    b = 1
}

考點:defer執行順序
解答:
這道題類似第1題 需要注意到defer執行順序和值傳遞 index:1肯定是最後執行的,但是index:1的第三個引數是一個函式,所以最先被呼叫
calc("10",1,2)==>10,1,2,3 執行index:2時,與之前一樣,需要先呼叫calc("20",0,2)==>20,0,2,2 執行到b=1時候開始呼叫,index:2==>calc("2",0,2)==>2,0,2,2最後執行index:1==>calc("1",1,3)==>1,1,3,4

10 1 2 320 0 2 22 0 2 21 1 3 4

7 請寫出以下輸入內容

funcmain() {    
 
      s := make([]int,5)
 
   s = append(s,1, 2, 3)
 
   fmt.Println(s)
}

考點:make預設值和append
解答:
make初始化是由預設值的哦,此處預設值為0

[00000123]

大家試試改為:

s := make([]int, 0)
s =
append(s, 1, 2, 3)
fmt.Println(s)
//[1 2 3]

8 下面的程式碼有什麼問題?

type UserAges struct {
 
   ages map[string]int
 
   sync.Mutex
}
func(ua*UserAges)Add(name string, age int) {
 
   ua.Lock()  
       deferua.Unlock()
 
   ua.ages[name] = age
}
func(ua*UserAges)Get(name string)int {    
 
     ifage, ok := ua.ages[name]; ok {        
 
        return age
 
   }  
      return-1
}

考點:map執行緒安全
解答:
可能會出現

fatal error: concurrent mapreadandmapwrite.

修改一下看看效果

func (ua *UserAges)Get(namestring)int {
 
   ua.Lock()    
     deferua.Unlock()    
 
    ifage, ok := ua.ages[name]; ok {        
 
         return age
 
   }    
       return-1
}

9.   下面的迭代會有什麼問題?

func (set *threadSafeSet)Iter()<-chaninterface{} {
 
   ch := make(chaninterface{})   
 
             gofunc() {
 
       set.RLock()   
            for elem := range set.s {
 
          ch <- elem
        }     
             close(ch)
 
       set.RUnlock()
    }()
     return ch
}

考點:chan快取池
解答:
看到這道題,我也在猜想出題者的意圖在哪裡。 chan?sync.RWMutex?go?chan快取池?迭代? 所以只能再讀一次題目,就從迭代入手看看。 既然是迭代就會要求set.s全部可以遍歷一次。但是chan是為快取的,那就代表這寫入一次就會阻塞。 我們把程式碼恢復為可以執行的方式,看看效果

package main
import (   
 
     "sync"
 
   "fmt")//下面的迭代會有什麼問題?type threadSafeSet struct {
 
   sync.RWMutex
    s []interface{}
}
func(set*threadSafeSet)Iter() <-chaninterface{} {    
//ch := make(chan interface{}) // 解除註釋看看!
 
   ch := make(chaninterface{},len(set.s))   
gofunc() {
 
       set.RLock()       
forelem,value := range set.s {
 
          ch <- elem            
println("Iter:",elem,value)
 
       }       close(ch)
 
       set.RUnlock()
    }()    
return ch
}
funcmain() {
 
   th:=threadSafeSet{
        s:[]interface{}{"1","2"},
 
   }
    v:=<-th.Iter()
    fmt.Sprintf("%s%v","ch",v)
}

10 以下程式碼能編譯過去嗎?為什麼?

package main
import (   "fmt")
typePeople interface {
 
   Speak(string) string
}
typeStduent struct{}
func(stu*Stduent)Speak(think string)(talk string) {    
ifthink == "bitch" {
 
       talk = "Youare a good boy"
 
   } else {
 
       talk = "hi"
 
   }
    return
}
funcmain() {
 
   var peoPeople = Stduent{}
 
   think := "bitch"
 
  fmt.Println(peo.Speak(think))
}

考點:golang的方法集
解答:
編譯不通過! 做錯了!?說明你對golang的方法集還有一些疑問。 一句話:golang的方法集僅僅影響介面實現和方法表示式轉化,與通過例項或者指標呼叫方法無關。

11 以下程式碼列印出來什麼內容,說出為什麼。

package main
import (   "fmt")
typePeople interface {
 
   Show()
}
typeStudent struct{}
func(stu*Student)Show() {
}
funclive()People {
 
   var stu*Student
 
   return stu
}
funcmain() {   if live() == nil
{
 
       fmt.Println("AAAAAAA")
 
   } else {
 
       fmt.Println("BBBBBBB")
 
   }
}

考點:interface內部結構
解答:
很經典的題! 這個考點是很多人忽略的interface內部結構。 go中的介面分為兩種一種是空的介面類似這樣:

varininterface{}

另一種如題目:

type People interface {
 
   Show()
}

他們的底層結構如下:

type eface struct {      //空介面
 
   _type *_type        //型別資訊
 
   data  unsafe.Pointer //指向資料的指標(go語言中特殊的指標型別unsafe.Pointer類似於c語言中的void*)}
typeiface struct {      //帶有方法的介面
 
   tab  *itab          //儲存type資訊還有結構實現方法的集合
 
   data unsafe.Pointer  //指向資料的指標(go語言中特殊的指標型別unsafe.Pointer類似於c語言中的void*)}
type_type struct {
 
   size       uintptr //型別大小
 
   ptrdata    uintptr //字首持有所有指標的記憶體大小
 
   hash       uint32  //資料hash
 
   tflag     tflag
    align      uint8   //對齊
 
   fieldalign uint8   //嵌入結構體時的對齊
 
   kind       uint8   //kind 有些列舉值kind等於0是無效的
 
   alg       *typeAlg //函式指標陣列,型別實現的所有方法
 
   gcdata    *byte   str       nameOff
 
   ptrToThis typeOff
}type itab struct {
 
   inter  *interfacetype //介面型別
 
   _type  *_type         //結構型別
 
   link   *itab
    bad    int32
 
   inhash int32
 
   fun    [1]uintptr     //可變大小方法集合}

可以看出iface比eface 中間多了一層itab結構。 itab 儲存_type資訊和[]fun方法集,從上面的結構我們就可得出,因為data指向了nil 並不代表interface 是nil, 所以返回值並不為空,這裡的fun(方法集)定義了介面的接收規則,在編譯的過程中需要驗證是否實現介面 結果:

BBBBBBB
12.是否可以編譯通過?如果通過,輸出什麼?

func main() {
 
   i := GetValue() switch i.(type) { 
 
       caseint:       
 
       println("int")   
 
       casestring:       
 
       println("string")   
 
       caseinterface{}:       
 
       println("interface")   
 
       default:       
 
        println("unknown")
 
   }
}
funcGetValue()int {   
return1
}

解析
考點:type

編譯失敗,因為type只能使用在interface

13.下面函式有什麼問題?

func funcMui(x,y int)(sum int,error){    
returnx+y,nil
}

解析
考點:函式返回值命名
在函式有多個返回值時,只要有一個返回值有指定命名,其他的也必須有命名。 如果返回值有有多個返回值必須加上括號; 如果只有一個返回值並且有命名也需要加上括號; 此處函式第一個返回值有sum名稱,第二個未命名,所以錯誤。

14.是否可以編譯通過?如果通過,輸出什麼?

package mainfunc main() {    println(DeferFunc1(1)) println(DeferFunc2(1)) println(DeferFunc3(1))
}
func DeferFunc1(i int)(t int) {
 
   t = i   deferfunc() {
 
       t += 3
 
   }() return t
}
funcDeferFunc2(i int)int {
 
   t := i  deferfunc() {
 
       t += 3
 
   }() return t
}
funcDeferFunc3(i int)(t int) {   deferfunc() {
 
       t += i
    }() return2}

解析
考點:defer和函式返回值
需要明確一點是defer需要在函式結束前執行。 函式返回值名字會在函式起始處被初始化為對應型別的零值並且作用域為整個函式 DeferFunc1有函式返回值t作用域為整個函式,在return之前defer會被執行,所以t會被修改,返回4; DeferFunc2函式中t的作用域為函式,返回1;DeferFunc3返回3

15.是否可以編譯通過?如果通過,輸出什麼?

funcmain() {    list := new([]int)
 
   list = append(list,1)
 
   fmt.Println(list)
}

解析
考點:new

list:=make([]int,0)

16.是否可以編譯通過?如果通過,輸出什麼?

package mainimport "fmt"funcmain() {
 
   s1 := []int{1, 2, 3}
 
   s2 := []int{4, 5}
 
   s1 = append(s1,s2)
 
   fmt.Println(s1)
}

解析
考點:append
append切片時候別漏了'…'

17.是否可以編譯通過?如果通過,輸出什麼?

func main() {
 
   sn1 := struct {
 
       age  int
 
       name string
 
   }{age: 11,name: "qq"}
 
   sn2 := struct {
 
       age  int
 
       name string
 
   }{age: 11,name: "qq"}  if sn1== sn2 {
 
       fmt.Println("sn1== sn2")
 
   }
    sm1 := struct {
 
       age int
 
       m   map[string]string
 
   }{age: 11, m:map[string]string{"a": "1"}}
 
   sm2 := struct {
 
       age int
 
       m   map[string]string
 
   }{age: 11, m:map[string]string{"a": "1"}} 
 
          if sm1 == sm2 {
 
       fmt.Println("sm1== sm2")
 
   }
}

解析
考點:結構體比較
進行結構體比較時候,只有相同型別的結構體才可以比較,結構體是否相同不但與屬性型別個數有關,還與屬性順序相關。

sn3:= struct {
 
   name string
 
   age  int
}
{age:
11,name:"qq"}

sn3與sn1就不是相同的結構體了,不能比較。 還有一點需要注意的是結構體是相同的,但是結構體屬性中有不可以比較的型別,如map,slice。 如果該結構屬性都是可以比較的,那麼就可以使用“==”進行比較操作。

可以使用reflect.DeepEqual進行比較

if reflect.DeepEqual(sn1, sm) {
 
   fmt.Println("sn1==sm")
}
else {
 
   fmt.Println("sn1!=sm")
}

所以編譯不通過: invalid operation: sm1 == sm2

18.是否可以編譯通過?如果通過,輸出什麼?

func Foo(x interface{}) {    if x== nil {
 
       fmt.Println("emptyinterface")      
 
         return
 
   }
    fmt.Println("non-emptyinterface")
}
 
      funcmain() {   
 
      var x *int = nil
 
   Foo(x)
}

解析
考點:interface內部結構

non-emptyinterface

19.是否可以編譯通過?如果通過,輸出什麼?

func GetValue(m map[int]string, id int)(string, bool) {    
 
        if _,exist := m[id]; exist {        
 
          return"存在資料", true
 
   }  
         returnnil, false}funcmain() {
 
   intmap:=map[int]string{   
1:"a",       
2:"bb",       
3:"ccc",
 
   }
    v,err:=GetValue(intmap,3)
 
   fmt.Println(v,err)
}

解析
考點:函式返回值型別
nil 可以用作 interface、function、pointer、map、slice 和 channel 的“空值”。但是如果不特別指定的話,Go 語言不能識別型別,所以會報錯。報:cannot use nil as type string in return argument.

20.是否可以編譯通過?如果通過,輸出什麼?

const (
 
   x = iota
 
   y
    z = "zz"
 
   k
    p = iota)
funcmain() 
{
 
   fmt.Println(x,y,z,k,p)
}

解析
考點:iota
結果:

0 1 zz zz 4

21.編譯執行下面程式碼會出現什麼?

package mainvar(
 
   size :=1024
 
   max_size = size*2)
funcmain() {    
println(size,max_size)
}

解析
考點:變數簡短模式
變數簡短模式限制:

定義變數同時顯式初始化
不能提供資料型別
只能在函式內部使用
結果:

syntaxerror: unexpected :=

22.下面函式有什麼問題?

package main
const cl = 100
var bl   = 123
funcmain() {    
println(&bl,bl)   
println(&cl,cl)
}

解析
考點:常量
常量不同於變數的在執行期分配記憶體,常量通常會被編譯器在預處理階段直接展開,作為指令資料使用,

cannot take the address of cl

23.編譯執行下面程式碼會出現什麼?

package main
funcmain() {    
for i:=0;i<10;i++  {
 
   loop:       
println(i)
 
   }    gotoloop
}

解析
考點:goto
goto不能跳轉到其他函式或者內層程式碼

goto loop jumps intoblock starting at

24.編譯執行下面程式碼會出現什麼?

package main
import"fmt"
funcmain() {    
 
typeMyInt1 int    
 
typeMyInt2 = int
 
   var i int =9
 
   var i1MyInt1 = i
 
   var i2MyInt2 = i
 
   fmt.Println(i1,i2)
}

解析
考點:**Go 1.9 新特性 Type Alias **
基於一個型別建立一個新型別,稱之為defintion;基於一個型別建立一個別名,稱之為alias。 MyInt1為稱之為defintion,雖然底層型別為int型別,但是不能直接賦值,需要強轉; MyInt2稱之為alias,可以直接賦值。

結果:

cannot use i (typeint) astype MyInt1 in assignment

25.編譯執行下面程式碼會出現什麼?

package main
import"fmt"
typeUser struct {
}
typeMyUser1 User
typeMyUser2 = User
func(iMyUser1)m1(){
 
   fmt.Println("MyUser1.m1")
}
func(iUser)m2(){
 
   fmt.Println("User.m2")
}
funcmain() {
 
   var i1MyUser1
 
   var i2MyUser2
 
   i1.m1()
    i2.m2()
}

解析
考點:**Go 1.9 新特性 Type Alias **
因為MyUser2完全等價於User,所以具有其所有的方法,並且其中一個新增了方法,另外一個也會有。 但是

i1.m2()

是不能執行的,因為MyUser1沒有定義該方法。 結果:

MyUser1.m1User.m2

26.編譯執行下面程式碼會出現什麼?

package main
import"fmt"
type T1 struct {
}
func(tT1)m1(){
 
   fmt.Println("T1.m1")
}
type T2= T1
typeMyStruct struct {
 
   T1
    T2
}
funcmain() {
 
   my:=MyStruct{}
    my.m1()
}

解析
考點:**Go 1.9 新特性 Type Alias **
是不能正常編譯的,異常:

ambiguousselectormy.m1

結果不限於方法,欄位也也一樣;也不限於type alias,type defintion也是一樣的,只要有重複的方法、欄位,就會有這種提示,因為不知道該選擇哪個。 改為:

my.T1.m1()
my.T2.m1()

type alias的定義,本質上是一樣的型別,只是起了一個別名,源型別怎麼用,別名型別也怎麼用,保留源型別的所有方法、欄位等。

27.編譯執行下面程式碼會出現什麼?

package main
import (   
 
      "errors"
 
   "fmt")
varErrDidNotWork = errors.New("did not work")
funcDoTheThing(reallyDoItbool)(errerror) {    
ifreallyDoIt {
 
       result, err:= tryTheThing()        
if err!= nil || result != "it worked" {
 
          err = ErrDidNotWork
        }
    }    return err
}
functryTheThing()(string,error) {    
return"",ErrDidNotWork
}
funcmain() {
 
   fmt.Println(DoTheThing(true))
 
   fmt.Println(DoTheThing(false))
}

解析
考點:變數作用域
因為 if 語句塊內的 err 變數會遮罩函式作用域內的 err 變數,結果:

改為:

func DoTheThing(reallyDoIt bool)(errerror) {    
varresult string
 
   ifreallyDoIt {
 
       result, err =tryTheThing()        
if err!= nil || result != "it worked" {
 
          err = ErrDidNotWork
        }
    }    return err
}

28.編譯執行下面程式碼會出現什麼?

package main
functest() []func() {    
varfuns []func()
 
   fori:=0;i<2;i++  {
 
       funs = append(funs,func() {           
 
          println(&i,i)
 
       })
    }    returnfuns
}
funcmain(){
 
   funs:=test()    
       for_,f:=range funs{
 
       f()
    }
}

解析
考點:閉包延遲求值
for迴圈複用區域性變數i,每一次放入匿名函式的應用都是想一個變數。 結果:

0xc042046000 2
0xc042046000 2

如果想不一樣可以改為:

func test() []func()  {    
varfuns []func()
 
   fori:=0;i<2;i++  {
 
       x:=i
        funs = append(funs,func() {           
println(&x,x)
 
       })
    }    returnfuns
}

29.編譯執行下面程式碼會出現什麼?

package main
functest(x int)(func(),func()) {    
returnfunc() {       
println(x)
 
   x+=10
 
   }, func() {       
 
     println(x)
 
   }
}
funcmain() {
 
   a,b:=test(100)
 
   a()
    b()
}

解析
考點:閉包引用相同變數*
結果:

100
110

30. 編譯執行下面程式碼會出現什麼?

package main
import (   "fmt"
 
   "reflect")
funcmain1() {    
deferfunc() {     
iferr:=recover();err!=nil{
 
         fmt.Println(err)
       }else {
 
         fmt.Println("fatal")
 
      }
    }()    
deferfunc() {       
panic("deferpanic")
 
   }()    
panic("panic")
}
funcmain() {    
deferfunc() {       
iferr:=recover();err!=nil{
 
          fmt.Println("++++")
 
          f:=err.(func()string)            
fmt.Println(err,f(),reflect.TypeOf(err).Kind().String())
 
       }else {
 
          fmt.Println("fatal")
 
       }
    }()    
deferfunc() {       
panic(func()string {           
return "defer panic"
 
       })
    }()    
panic("panic")
}

解析
考點:panic僅有最後一個可以被revover捕獲
觸發panic("panic")後順序執行defer,但是defer中還有一個panic,所以覆蓋了之前的panic("panic")

 




網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN



相關文章