能力模型
級別 | 模型 |
---|---|
初級 primary | 熟悉基本語法,能夠看懂代碼的意圖;在他人指導下能夠完成用戶故事的開發,編寫的代碼符合CleanCode規範; |
中級 intermediate | 能夠獨立完成用戶故事的開發和測試;能夠嗅出代碼的壞味道,並知道如何重構達成目標; |
高級 senior | 能夠開發出高質量高性能的代碼;能夠熟練使用高級特性,開發編程框架或測試框架; |
選擇題
- 【初級】下面屬於關鍵字的是()
A. func
B. def
C. struct
D. class
參考答案:AC
- 【初級】定義一個包內全局字符串變量,下面語法正確的是()
A. var str string
B. str := “”
C. str = “”
D. var str = “”
參考答案:AD
- 【初級】通過指針變量 p 訪問其成員變量 name,下面語法正確的是()
A. p.name
B. (*p).name
C. (&p).name
D. p->name
參考答案:AB
- 【初級】關於接口和類的說法,下面說法正確的是()
A. 一個類只需要實現了接口要求的所有函數,我們就說這個類實現了該接口
B. 實現類的時候,只需要關心自己應該提供哪些方法,不用再糾結接口需要拆得多細才合理
C. 類實現接口時,需要導入接口所在的包
D. 接口由使用方按自身需求來定義,使用方無需關心是否有其他模塊定義過類似的接口
參考答案:ABD
- 【初級】關於字符串連接,下面語法正確的是()
A. str := ‘abc’ + ‘123’
B. str := “abc” + “123”
C. str := ‘123’ + “abc”
D. fmt.Sprintf(“abc%d”, 123)
參考答案:BD
- 【初級】關於協程,下面說法正確是()
A. 協程和線程都可以實現程序的併發執行
B. 線程比協程更輕量級
C. 協程不存在死鎖問題
D. 通過channel來進行協程間的通信
參考答案:AD
- 【中級】關於init函數,下面說法正確的是()
A. 一個包中,可以包含多個init函數
B. 程序編譯時,先執行導入包的init函數,再執行本包內的init函數
C. main包中,不能有init函數
D. init函數可以被其他函數調用
參考答案:AB
- 【初級】關於循環語句,下面說法正確的有()
A. 循環語句既支持for關鍵字,也支持while和do-while
B. 關鍵字for的基本使用方法與C/C++中沒有任何差異
C. for循環支持continue和break來控制循環,但是它提供了一個更高級的break,可以選擇中斷哪一個循環
D. for循環不支持以逗號爲間隔的多個賦值語句,必須使用平行賦值的方式來初始化多個變量
參考答案:CD
- 【中級】對於函數定義:
func add(args ...int) int {
sum :=0
for _, arg := range args {
sum += arg
}
return sum
}
下面對add函數調用正確的是()
A. add(1, 2)
B. add(1, 3, 7)
C. add([]int{1, 2})
D. add([]int{1, 3, 7}…)
參考答案:ABD
- 【初級】關於類型轉化,下面語法正確的是()
A.
type MyInt int
var i int = 1
var j MyInt = i
B.
type MyInt int
var i int= 1
var j MyInt = (MyInt)i
C.
type MyInt int
var i int= 1
var j MyInt = MyInt(i)
D.
type MyInt int
var i int= 1
var j MyInt = i.(MyInt)
參考答案:C
- 【初級】關於局部變量的初始化,下面正確的使用方式是()
A. var i int = 10
B. var i = 10
C. i := 10
D. i = 10
參考答案:ABC
- 【初級】關於const常量定義,下面正確的使用方式是()
A.
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
- 【初級】關於布爾變量b的賦值,下面錯誤的用法是()
A. b = true
B. b = 1
C. b = bool(1)
D. b = (1 == 2)
參考答案:BC
- 【中級】下面的程序的運行結果是()
func main() {
if (true) {
defer fmt.Printf("1")
} else {
defer fmt.Printf("2")
}
fmt.Printf("3")
}
A. 321
B. 32
C. 31
D. 13
參考答案:C
- 【初級】關於switch語句,下面說法正確的有()
A. 條件表達式必須爲常量或者整數
B. 單個case中,可以出現多個結果選項
C. 需要用break來明確退出一個case
D. 只有在case中明確添加fallthrough關鍵字,纔會繼續執行緊跟的下一個case
參考答案:BD
- 【中級】 golang中沒有隱藏的this指針,這句話的含義是()
A. 方法施加的對象顯式傳遞,沒有被隱藏起來
B. golang沿襲了傳統面向對象編程中的諸多概念,比如繼承、虛函數和構造函數
C. golang的面向對象表達更直觀,對於面向過程只是換了一種語法形式來表達
D. 方法施加的對象不需要非得是指針,也不用非得叫this
參考答案:ACD
- 【中級】 golang中的引用類型包括()
A. 數組切片
B. map
C. channel
D. interface
參考答案:ABCD
- 【中級】 golang中的指針運算包括()
A. 可以對指針進行自增或自減運算
B. 可以通過“&”取指針的地址
C. 可以通過“*”取指針指向的數據
D. 可以對指針進行下標運算
參考答案:BC
- 【初級】關於main函數(可執行程序的執行起點),下面說法正確的是()
A. main函數不能帶參數
B. main函數不能定義返回值
C. main函數所在的包必須爲main包
D. main函數中可以使用flag包來獲取和解析命令行參數
參考答案:ABCD
- 【中級】下面賦值正確的是()
A. var x = nil
B. var x interface{} = nil
C. var x string = nil
D. var x error = nil
參考答案:BD
- 【中級】關於整型切片的初始化,下面正確的是()
A. s := make([]int)
B. s := make([]int, 0)
C. s := make([]int, 5, 10)
D. s := []int{1, 2, 3, 4, 5}
參考答案:BCD
- 【中級】從切片中刪除一個元素,下面的算法實現正確的是()
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
}
}
return ERR_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
}
}
return ERR_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
}
}
return ERR_ELEM_NT_EXIST
}
參考答案:D
- 【初級】對於局部變量整型切片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
- 【初級】關於變量的自增和自減操作,下面語句正確的是()
A.
i := 1
i ++
B.
i := 1
j = i ++
C.
i := 1
++ i
D.
i := 1
i --
參考答案:AD
- 【中級】關於函數聲明,下面語法錯誤的是()
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
- 【中級】如果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
- 【中級】如果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.
typeInteger 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
}
參考答案:A
- 【中級】關於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
- 【中級】關於GoMock,下面說法正確的是()
A. GoMock可以對interface打樁
B. GoMock可以對類的成員函數打樁
C. GoMock可以對函數打樁
D. GoMock打樁後的依賴注入可以通過GoStub完成
參考答案:AD
- 【中級】關於接口,下面說法正確的是()
A. 只要兩個接口擁有相同的方法列表(次序不同不要緊),那麼它們就是等價的,可以相互賦值
B. 如果接口A的方法列表是接口B的方法列表的子集,那麼接口B可以賦值給接口A
C. 接口查詢是否成功,要在運行期才能夠確定
D. 接口賦值是否可行,要在運行期才能夠確定
參考答案:ABC
- 【初級】關於channel,下面語法正確的是()
A. var ch chan int
B. ch := make(chan int)
C. <- ch
D. ch <-
參考答案:ABC
- 【初級】關於同步鎖,下面說法正確的是()
A. 當一個goroutine獲得了Mutex後,其他goroutine就只能乖乖的等待,除非該goroutine釋放這個Mutex
B. RWMutex在讀鎖佔用的情況下,會阻止寫,但不阻止讀
C. RWMutex在寫鎖佔用情況下,會阻止任何其他goroutine(無論讀和寫)進來,整個鎖相當於由該goroutine獨佔
D. Lock()操作需要保證有Unlock()或RUnlock()調用與之對應
參考答案:ABC
- 【中級】 golang中大多數數據類型都可以轉化爲有效的JSON文本,下面幾種類型除外()
A. 指針
B. channel
C. complex
D. 函數
參考答案:BCD
- 【中級】關於go vendor,下面說法正確的是()
A. 基本思路是將引用的外部包的源代碼放在當前工程的vendor目錄下面
B. 編譯go代碼會優先從vendor目錄先尋找依賴包
C. 可以指定引用某個特定版本的外部包
D. 有了vendor目錄後,打包當前的工程代碼到其他機器的$GOPATH/src下都可以通過編譯
參考答案:ABD
- 【初級】 flag是bool型變量,下面if表達式符合編碼規範的是()
A. if flag == 1
B. if flag
C. if flag == false
D. if !flag
參考答案:BD
- 【初級】 value是整型變量,下面if表達式符合編碼規範的是()
A. if value == 0
B. if value
C. if value != 0
D. if !value
參考答案:AC
- 【中級】關於函數返回值的錯誤設計,下面說法正確的是()
A. 如果失敗原因只有一個,則返回bool
B. 如果失敗原因超過一個,則返回error
C. 如果沒有失敗原因,則不返回bool或error
D. 如果重試幾次可以避免失敗,則不要立即返回bool或error
參考答案:ABCD
- 【中級】關於異常設計,下面說法正確的是()
A. 在程序開發階段,堅持速錯,讓程序異常崩潰
B. 在程序部署後,應恢復異常避免程序終止
C. 一切皆錯誤,不用進行異常設計
D. 對於不應該出現的分支,使用異常處理
參考答案:ABD
- 【中級】關於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
- 【中級】關於channel的特性,下面說法正確的是()
A. 給一個 nil channel 發送數據,造成永遠阻塞
B. 從一個 nil channel 接收數據,造成永遠阻塞
C. 給一個已經關閉的 channel 發送數據,引起 panic
D. 從一個已經關閉的 channel 接收數據,如果緩衝區中爲空,則返回一個零值
參考答案:ABCD
- 【中級】關於無緩衝和有衝突的channel,下面說法正確的是()
A. 無緩衝的channel是默認的緩衝爲1的channel
B. 無緩衝的channel和有緩衝的channel都是同步的
C. 無緩衝的channel和有緩衝的channel都是非同步的
D. 無緩衝的channel是同步的,而有緩衝的channel是非同步的
參考答案:D
- 【中級】關於異常的觸發,下面說法正確的是()
A. 空指針解析
B. 下標越界
C. 除數爲0
D. 調用panic函數
參考答案:ABCD
- 【中級】關於cap函數的適用類型,下面說法正確的是()
A. array
B. slice
C. map
D. channel
參考答案:ABD
- 【中級】關於beego框架,下面說法正確的是()
A. beego是一個golang實現的輕量級HTTP框架
B. beego可以通過註釋路由、正則路由等多種方式完成url路由注入
C. 可以使用bee new工具生成空工程,然後使用bee run命令自動熱編譯
D. beego框架只提供了對url路由的處理,而對於MVC架構中的數據庫部分未提供框架支持
參考答案:ABC
- 【中級】關於goconvey,下面說法正確的是()
A. goconvey是一個支持golang的單元測試框架
B. goconvey能夠自動監控文件修改並啓動測試,並可以將測試結果實時輸出到web界面
C. goconvey提供了豐富的斷言簡化測試用例的編寫
D. goconvey無法與go test集成
參考答案:ABC
- 【中級】關於go vet,下面說法正確的是()
A. go vet是golang自帶工具go tool vet的封裝
B. 當執行go vet database時,可以對database所在目錄下的所有子文件夾進行遞歸檢測
C. go vet可以使用絕對路徑、相對路徑或相對GOPATH的路徑指定待檢測的包
D. go vet可以檢測出死代碼
參考答案:ACD
- 【中級】關於map,下面說法正確的是()
A. map反序列化時json.unmarshal的入參必須爲map的地址
B. 在函數調用中傳遞map,則子函數中對map元素的增加不會導致父函數中map的修改
C. 在函數調用中傳遞map,則子函數中對map元素的修改不會導致父函數中map的修改
D. 不能使用內置函數delete刪除map的元素
參考答案:A
- 【中級】關於GoStub,下面說法正確的是()
A. GoStub可以對全局變量打樁
B. GoStub可以對函數打樁
C. GoStub可以對類的成員方法打樁
D. GoStub可以打動態樁,比如對一個函數打樁後,多次調用該函數會有不同的行爲
參考答案:ABD
- 【初級】關於select機制,下面說法正確的是()
A. select機制用來處理異步IO問題
B. select機制最大的一條限制就是每個case語句裏必須是一個IO操作
C. golang在語言級別支持select關鍵字
D. select關鍵字的用法與switch語句非常類似,後面要帶判斷條件
參考答案:ABC
- 【初級】關於內存泄露,下面說法正確的是()
A. golang有自動垃圾回收,不存在內存泄露
B. golang中檢測內存泄露主要依靠的是pprof包
C. 內存泄露可以在編譯階段發現
D. 應定期使用瀏覽器來查看系統的實時內存信息,及時發現內存泄露問題
參考答案:BD
填空題
- 【初級】聲明一個整型變量i__________
參考答案:var i int
- 【初級】聲明一個含有10個元素的整型數組a__________
參考答案:var a [10]int
- 【初級】聲明一個整型數組切片s__________
參考答案:var s []int
- 【初級】聲明一個整型指針變量p__________
參考答案:var p *int
- 【初級】聲明一個key爲字符串型value爲整型的map變量m__________
參考答案:var m map[string]int
- 【初級】聲明一個入參和返回值均爲整型的函數變量f__________
參考答案:var f func(a int) int
- 【初級】聲明一個只用於讀取int數據的單向channel變量ch__________
參考答案:var ch <- chan int
- 【初級】假設源文件的命名爲slice.go,則測試文件的命名爲__________
參考答案:slice_test.go
- 【初級】 go test要求測試函數的前綴必須命名爲__________
參考答案:Test
- 【中級】下面的程序的運行結果是__________
for i:=0; i < 5; i++ {
defer fmt.Printf("%d ", i)
}
參考答案:4 3 2 1 0
- 【中級】下面的程序的運行結果是__________
func main() {
x := 1
{
x := 2
fmt.Print(x)
}
fmt.Println(x)
}
參考答案:21
- 【中級】下面的程序的運行結果是__________
func main() {
strs := []string{"one","two", "three"}
for _, s := range strs {
go func() {
time.Sleep(1 * time.Second)
fmt.Printf("%s ", s)
}()
}
time.Sleep(3 * time.Second)
}
參考答案: three three three
- 【中級】下面的程序的運行結果是__________
func main() {
x := []string{"a", "b","c"}
for v := range x {
fmt.Print(v)
}
}
參考答案:012
- 【中級】下面的程序的運行結果是__________
func main() {
x := []string{"a", "b","c"}
for _, v := range x {
fmt.Print(v)
}
}
參考答案:abc
- 【初級】下面的程序的運行結果是__________
func main() {
i := 1
j := 2
i, j = j, i
fmt.Printf("%d%d\n", i, j)
}
參考答案:21
- 【初級】下面的程序的運行結果是__________
func incr(p *int) int {
*p ++
return *p
}
func main() {
v := 1
incr(&v)
fmt.Println(v)
}
參考答案:2
- 【初級】啓動一個goroutine的關鍵字是__________
參考答案:go
- 【中級】下面的程序的運行結果是__________
type Slice []int
func NewSlice() Slice {
return make(Slice, 0)
}
func (s* Slice) Add(elem int) *Slice {
*s = append(*s, elem)
fmt.Print(elem)
return s
}
func main() {
s := NewSlice()
defer s.Add(1).Add(2)
s.Add(3)
}
參考答案:132
判斷題
- 【初級】數組是一個值類型()
參考答案:T
- 【初級】使用map不需要引入任何庫()
參考答案:T
- 【中級】內置函數delete可以刪除數組切片內的元素()
參考答案:F
- 【初級】指針是基礎類型()
參考答案:F
- 【初級】 interface{}是可以指向任意對象的Any類型()
參考答案:T
- 【中級】下面關於文件操作的代碼可能觸發異常()
file, err := os.Open("test.go")
defer file.Close()
if err != nil {
fmt.Println("open file failed:",err)
return
}
參考答案:T
- 【初級】 Golang不支持自動垃圾回收()
參考答案:F
- 【初級】 Golang支持反射,反射最常見的使用場景是做對象的序列化()
參考答案:T
- 【初級】 Golang可以複用C/C++的模塊,這個功能叫Cgo()
參考答案:F
- 【初級】下面代碼中兩個斜點之間的代碼,比如json:“x”,作用是X字段在從結構體實例編碼到JSON數據格式的時候,使用x作爲名字,這可以看作是一種重命名的方式()
type Position struct {
X int `json:"x"`
Y int `json:"y"`
Z int `json:"z"`
}
參考答案:T
- 【初級】通過成員變量或函數首字母的大小寫來決定其作用域()
參考答案:T
- 【初級】對於常量定義zero(const zero = 0.0),zero是浮點型常量()
參考答案:F
- 【初級】對變量x的取反操作是~x()
參考答案:F
- 【初級】下面的程序的運行結果是xello()
func main() {
str := "hello"
str[0] = "x"
fmt.Println(str)
}
參考答案:F
- 【初級】 golang支持goto語句()
參考答案:T
- 【初級】下面代碼中的指針p爲野指針,因爲返回的棧內存在函數結束時會被釋放()
type TimesMatcher struct {
base int
}
func NewTimesMatcher(base int) *TimesMatcher{
return &TimesMatcher{base:base}
}
func main() {
p := NewTimesMatcher(3)
...
}
參考答案:F
- 【初級】匿名函數可以直接賦值給一個變量或者直接執行()
參考答案:T
- 【初級】如果調用方調用了一個具有多返回值的方法,但是卻不想關心其中的某個返回值,可以簡單地用一個下劃線“_”來跳過這個返回值,該下劃線對應的變量叫匿名變量()
參考答案:T
- 【初級】在函數的多返回值中,如果有error或bool類型,則一般放在最後一個()
參考答案:T
- 【初級】錯誤是業務過程的一部分,而異常不是()
參考答案:T
- 【初級】函數執行時,如果由於panic導致了異常,則延遲函數不會執行()
參考答案:F
- 【中級】當程序運行時,如果遇到引用空指針、下標越界或顯式調用panic函數等情況,則先觸發panic函數的執行,然後調用延遲函數。調用者繼續傳遞panic,因此該過程一直在調用棧中重複發生:函數停止執行,調用延遲執行函數。如果一路在延遲函數中沒有recover函數的調用,則會到達該攜程的起點,該攜程結束,然後終止其他所有攜程,其他攜程的終止過程也是重複發生:函數停止執行,調用延遲執行函數()
參考答案:F
- 【初級】同級文件的包名不允許有多個()
參考答案:T
- 【中級】可以給任意類型添加相應的方法()
參考答案:F
- 【初級】 golang雖然沒有顯式的提供繼承語法,但是通過匿名組合實現了繼承()
參考答案:T
- 【初級】使用for range迭代map時每次迭代的順序可能不一樣,因爲map的迭代是隨機的()
參考答案:T
- 【初級】 switch後面可以不跟表達式()
參考答案:T
- 【中級】結構體在序列化時非導出變量(以小寫字母開頭的變量名)不會被encode,因此在decode時這些非導出變量的值爲其類型的零值()
參考答案:T
- 【初級】 golang中沒有構造函數的概念,對象的創建通常交由一個全局的創建函數來完成,以NewXXX來命名()
參考答案:T
- 【中級】當函數deferDemo返回失敗時,並不能destroy已create成功的資源()
func deferDemo() error {
err := createResource1()
if err != nil {
return ERR_CREATE_RESOURCE1_FAILED
}
defer func() {
if err != nil {
destroyResource1()
}
}()
err = createResource2()
if err != nil {
return ERR_CREATE_RESOURCE2_FAILED
}
defer func() {
if err != nil {
destroyResource2()
}
}()
err = createResource3()
if err != nil {
return ERR_CREATE_RESOURCE3_FAILED
}
return nil
}
參考答案:F
- 【中級】 channel本身必然是同時支持讀寫的,所以不存在單向channel()
參考答案:F
- 【初級】 import後面的最後一個元素是包名()
參考答案:F
面試題
1 寫出下面代碼輸出內容。
package main
import (
"fmt"
)
func main() {
defer_call()
}
func defer_call() {
defer func() {fmt.Println("打印前")}()
defer func() {fmt.Println("打印中")}()
defer func() {fmt.Println("打印後")}()
panic("觸發異常")
}
考點:defer執行順序
解答:
defer 是後進先出。
panic 需要等defer 結束後纔會向上傳遞。 出現panic恐慌時候,會先按照defer的後入先出的順序執行,最後纔會執行panic。
打印後
打印中
打印前
panic: 觸發異常
2 以下代碼有什麼問題,說明原因。
type student struct {
Name string
Age int
}
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
}
}
考點:foreach
解答:
這樣的寫法初學者經常會遇到的,很危險! 與Java的foreach一樣,都是使用副本的方式。所以m[stu.Name]=&stu實際上一致指向同一個指針, 最終該指針的值爲遍歷的最後一個struct的值拷貝。 就像想修改切片元素的屬性:
for _, stu := range stus {
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
}
for k, v := range m{
println(k, "=>", v.Name)
}
// 正確
for i := 0; i < len(stus); i++ {
m[stus[i].Name] = &stus[i]
}
for k, v := range m{
println(k, "=>", v.Name)
}
}
- 下面的代碼會輸出什麼,並說明原因
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內部指向值拷貝地址。
- 下面代碼會輸出什麼?
type People struct{}
func (p *People) ShowA() {
fmt.Println("showA")
p.ShowB()
}
func (p *People) ShowB() {
fmt.Println("showB")
}
type Teacher struct {
People
}
func (t *Teacher) ShowB() {
fmt.Println("teachershowB")
}
func main() {
t := Teacher{}
t.ShowA()
}
考點:go的組合繼承
解答:
這是Golang的組合模式,可以實現OOP的繼承。 被組合的類型People所包含的方法雖然升級成了外部類型Teacher這個組合類型的方法(一定要是匿名字段),但它們的方法(ShowA())調用時接受者並沒有發生變化。 此時People類型並不知道自己會被什麼類型組合,當然也就無法調用方法時去使用未知的組合者Teacher類型的功能。
showA
showB
- 下面代碼會觸發異常嗎?請詳細說明
func main() {
runtime.GOMAXPROCS(1)
int_chan := make(chan int, 1)
string_chan := make(chan string, 1)
int_chan <- 1
string_chan <- "hello"
select {
case value := <-int_chan:
fmt.Println(value)
case value := <-string_chan:
panic(value)
}
}
考點:select隨機性
解答:
select會隨機選擇一個可用通用做收發操作。 所以代碼是有肯觸發異常,也有可能不會。 單個chan如果無緩衝時,將會阻塞。但結合 select可以在多個chan間等待執行。有三點原則:
select 中只要有一個case能return,則立刻執行。
當如果同一時間有多個case均能return則僞隨機方式抽取任意一個執行。
如果沒有一個case能return則可以執行”default”塊。
- 下面代碼輸出什麼?
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
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 3
20 0 2 2
2 0 2 2
1 1 3 4
- 請寫出以下輸入內容
func main() {
s := make([]int, 5)
s = append(s, 1, 2, 3)
fmt.Println(s)
}
考點:make默認值和append
解答:
make初始化是由默認值的哦,此處默認值爲0
[0 0 0 0 0 1 2 3]
大家試試改爲:
s := make([]int, 0)
s = append(s, 1, 2, 3)
fmt.Println(s) //[1 2 3]
- 下面的代碼有什麼問題?
type UserAges struct {
ages map[string]int
sync.Mutex
}
func (ua *UserAges) Add(name string, age int) {
ua.Lock()
defer ua.Unlock()
ua.ages[name] = age
}
func (ua *UserAges) Get(name string)int {
if age, ok := ua.ages[name]; ok {
return age
}
return -1
}
考點:map線程安全
解答:
可能會出現
fatal error: concurrent mapreadandmapwrite.
修改一下看看效果
func (ua *UserAges) Get(namestring) int {
ua.Lock()
deferua.Unlock()
if age, ok := ua.ages[name]; ok {
return age
}
return -1
}
- 下面的迭代會有什麼問題?
func (set *threadSafeSet) Iter() <-chan interface{} {
ch := make(chan interface{})
go func() {
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() <-chan interface{} {
//ch := make(chan interface{}) // 解除註釋看看!
ch := make(chan interface{},len(set.s))
go func() {
set.RLock()
for elem, value := range set.s {
ch <- elem
println("Iter:",elem,value)
}
close(ch)
set.RUnlock()
}()
return ch
}
func main() {
th := threadSafeSet{
s: []interface{}{"1","2"},
}
v := <-th.Iter()
fmt.Sprintf("%s%v","ch",v)
}
- 以下代碼能編譯過去嗎?爲什麼?
package main
import (
"fmt"
)
type People interface {
Speak(string) string
}
type Stduent struct{}
func (stu *Stduent) Speak(think string) (talk string) {
if think == "bitch" {
talk = "You are a good boy"
} else {
talk = "hi"
}
return
}
func main() {
var peo People = Stduent{}
think := "bitch"
fmt.Println(peo.Speak(think))
}
考點:golang的方法集
解答:
編譯不通過! 做錯了!?說明你對golang的方法集還有一些疑問。 一句話:golang的方法集僅僅影響接口實現和方法表達式轉化,與通過實例或者指針調用方法無關。
- 以下代碼打印出來什麼內容,說出爲什麼。
package main
import (
"fmt"
)
type People interface {
Show()
}
type Student struct{}
func (stu *Student) Show() {
}
func live() People {
var stu *Student
return stu
}
func main() {
if live() == nil {
fmt.Println("AAAAAAA")
} else {
fmt.Println("BBBBBBB")
}
}
考點:interface內部結構
解答:
很經典的題! 這個考點是很多人忽略的interface內部結構。 go中的接口分爲兩種一種是空的接口類似這樣:
var ininterface{}
另一種如題目:
type People interface {
Show()
}
他們的底層結構如下:
type eface struct { //空接口
_type *_type //類型信息
data unsafe.Pointer //指向數據的指針(go語言中特殊的指針類型unsafe.Pointer類似於c語言中的void*)
}
type iface 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
- 是否可以編譯通過?如果通過,輸出什麼?
func main() {
i := GetValue()
switch i.(type) {
case int:
println("int")
case string:
println("string")
case interface{}:
println("interface")
default:
println("unknown")
}
}
func GetValue() int {
return 1
}
解析
考點:type
編譯失敗,因爲type只能使用在interface
- 下面函數有什麼問題?
func Mui(x,y int)(sum int,error){
return x+y,nil
}
解析
考點:函數返回值命名
在函數有多個返回值時,只要有一個返回值有指定命名,其他的也必須有命名。 如果返回值有有多個返回值必須加上括號; 如果只有一個返回值並且有命名也需要加上括號; 此處函數第一個返回值有sum名稱,第二個未命名,所以錯誤。
- 是否可以編譯通過?如果通過,輸出什麼?
package main
func main() {
println(DeferFunc1(1))
println(DeferFunc2(1))
println(DeferFunc3(1))
}
func DeferFunc1(i int)(t int) {
t = i
defer func() {
t += 3
}()
return t
}
func DeferFunc2(i int) int {
t := i
defer func() {
t += 3
}()
return t
}
func DeferFunc3(i int)(t int) {
defer func() {
t += i
}()
return 2
}
解析
考點:defer和函數返回值
需要明確一點是defer需要在函數結束前執行。 函數返回值名字會在函數起始處被初始化爲對應類型的零值並且作用域爲整個函數 DeferFunc1有函數返回值t作用域爲整個函數,在return之前defer會被執行,所以t會被修改,返回4; DeferFunc2函數中t的作用域爲函數,返回1;DeferFunc3返回3
- 是否可以編譯通過?如果通過,輸出什麼?
func main() {
list := new([]int)
list = append(list,1)
fmt.Println(list)
}
解析
考點:new
list := make([]int,0)
- 是否可以編譯通過?如果通過,輸出什麼?
package main
import "fmt"
func main() {
s1 := []int{1, 2, 3}
s2 := []int{4, 5}
s1 = append(s1, s2)
fmt.Println(s1)
}
解析
考點:append
append切片時候別漏了’…’
- 是否可以編譯通過?如果通過,輸出什麼?
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
- 是否可以編譯通過?如果通過,輸出什麼?
func Foo(x interface{}) {
if x == nil {
fmt.Println("emptyinterface")
return
}
fmt.Println("non-emptyinterface")
}
func main() {
var x *int = nil
Foo(x)
}
解析
考點:interface內部結構
non-emptyinterface
- 是否可以編譯通過?如果通過,輸出什麼?
func GetValue(m map[int]string, id int)(string, bool) {
if _, exist := m[id]; exist {
return "存在數據", true
}
return nil, false
}
func main() {
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.
- 是否可以編譯通過?如果通過,輸出什麼?
const (
x = iota
y
z = "zz"
k
p = iota)
func main() {
fmt.Println(x,y,z,k,p)
}
解析
考點:iota
結果:
0 1 zz zz 4
- 編譯執行下面代碼會出現什麼?
package main
var(
size :=1024
max_size = size*2)
func main() {
println(size,max_size)
}
解析
考點:變量簡短模式
變量簡短模式限制:
定義變量同時顯式初始化
不能提供數據類型
只能在函數內部使用
結果:
syntaxerror: unexpected :=
- 下面函數有什麼問題?
package main
const cl = 100
var bl = 123
func main() {
println(&bl, bl)
println(&cl, cl)
}
解析
考點:常量
常量不同於變量的在運行期分配內存,常量通常會被編譯器在預處理階段直接展開,作爲指令數據使用,
cannot take the address of cl
- 編譯執行下面代碼會出現什麼?
package main
func main() {
for i := 0; i < 10; i++ {
loop:
println(i)
}
goto loop
}
解析
考點:goto
goto不能跳轉到其他函數或者內層代碼
goto loop jumps intoblock starting at
- 編譯執行下面代碼會出現什麼?
package main
import "fmt"
func main() {
type MyInt1 int
type MyInt2 = int
var i int = 9
var i1 MyInt1 = i
var i2 MyInt2 = 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
- 編譯執行下面代碼會出現什麼?
package main
import "fmt"
type User struct {}
type MyUser1 User
type MyUser2 = User
func(i MyUser1)m1() {
fmt.Println("MyUser1.m1")
}
func(i User)m2() {
fmt.Println("User.m2")
}
func main() {
var i1 MyUser1
var i2 MyUser2
i1.m1()
i2.m2()
}
解析
考點:**Go 1.9 新特性 Type Alias **
因爲MyUser2完全等價於User,所以具有其所有的方法,並且其中一個新增了方法,另外一個也會有。 但是
i1.m2()
是不能執行的,因爲MyUser1沒有定義該方法。 結果:
MyUser1.m1
User.m2
- 編譯執行下面代碼會出現什麼?
package main
import "fmt"
type T1 struct {}
func (t T1) m1() {
fmt.Println("T1.m1")
}
type T2 = T1
type MyStruct struct {
T1
T2
}
func main() {
my := MyStruct{}
my.m1()
}
解析
考點:**Go 1.9 新特性 Type Alias **
是不能正常編譯的,異常:
ambiguous selector my.m1
結果不限於方法,字段也也一樣;也不限於type alias,type defintion也是一樣的,只要有重複的方法、字段,就會有這種提示,因爲不知道該選擇哪個。 改爲:
my.T1.m1()
my.T2.m1()
type alias的定義,本質上是一樣的類型,只是起了一個別名,源類型怎麼用,別名類型也怎麼用,保留源類型的所有方法、字段等。
- 編譯執行下面代碼會出現什麼?
package main
import (
"errors"
"fmt"
)
var ErrDidNotWork = errors.New("did not work")
func DoTheThing(reallyDoIt bool)(err error) {
if reallyDoIt {
result, err := tryTheThing()
if err != nil || result != "it worked" {
err = ErrDidNotWork
}
}
return err
}
func tryTheThing()(string, error) {
return "", ErrDidNotWork
}
func main() {
fmt.Println(DoTheThing(true))
fmt.Println(DoTheThing(false))
}
解析
考點:變量作用域
因爲 if 語句塊內的 err 變量會遮罩函數作用域內的 err 變量,結果:
<nil>
<nil>
改爲:
func DoTheThing(reallyDoIt bool)(err error) {
var result string
if reallyDoIt {
result, err = tryTheThing()
if err!= nil || result != "it worked" {
err = ErrDidNotWork
}
}
return err
}
- 編譯執行下面代碼會出現什麼?
package main
func test() []func() {
var funs []func()
for i := 0; i < 2; i++ {
funs = append(funs, func() {
println(&i, i)
})
}
return funs
}
func main() {
funs := test()
for _, f := range funs {
f()
}
}
解析
考點:閉包延遲求值
for循環複用局部變量i,每一次放入匿名函數的應用都是想一個變量。 結果:
0xc042046000 2
0xc042046000 2
如果想不一樣可以改爲:
func test() []func() {
var funs []func()
for i := 0; i < 2; i++ {
x := i
funs = append(funs,func() {
println(&x,x)
})
}
return funs
}
- 編譯執行下面代碼會出現什麼?
package main
func test(x int)(func(),func()) {
return func() {
println(x)
x += 10
}, func() {
println(x)
}
}
func main() {
a, b := test(100)
a()
b()
}
解析
考點:閉包引用相同變量*
結果:
100
110
- 編譯執行下面代碼會出現什麼?
package main
import (
"fmt"
"reflect"
)
func main1() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
} else {
fmt.Println("fatal")
}
}()
defer func() {
panic("deferpanic")
}()
panic("panic")
}
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println("++++")
f := err.(func()string)
fmt.Println(err, f(), reflect.TypeOf(err).Kind().String())
}else {
fmt.Println("fatal")
}
}()
defer func() {
panic(func()string {
return "defer panic"
})
}()
panic("panic")
}
解析
考點:panic僅有最後一個可以被revover捕獲
觸發panic(“panic”)後順序執行defer,但是defer中還有一個panic,所以覆蓋了之前的panic(“panic”)
————————————————
面試題來自原文鏈接:https://blog.csdn.net/itcastcpp/article/details/80462619