GO語(yǔ)言面試系列:(二)常規(guī)性Golang面試題解析

最近在很多地方看到了golang的面試題鳞上,看到了很多人對(duì)Golang的面試題心存恐懼粒没,也是為了復(fù)習(xí)基礎(chǔ)揩抡,我把解題的過程總結(jié)下來曲管。

面試題

1. 寫出下面代碼輸出內(nèi)容却邓。

package main

import (
    "fmt"
)

func main() {
    defer_call()
}

func defer_call() {
    defer func() { fmt.Println("打印前") }()
    defer func() { fmt.Println("打印中") }()
    defer func() { fmt.Println("打印后") }()

    panic("觸發(fā)異常")
}

考點(diǎn):defer執(zhí)行順序
解答:defer 是后進(jìn)先出。panic 需要等defer 結(jié)束后才會(huì)向上傳遞院水。
出現(xiàn)panic恐慌時(shí)候腊徙,會(huì)先按照defer的后入先出的順序執(zhí)行,最后才會(huì)執(zhí)行panic檬某。

打印后
打印中
打印前
panic: 觸發(fā)異常

近期有同學(xué)遇到多次執(zhí)行的時(shí)候發(fā)現(xiàn)panic的執(zhí)行順序不定撬腾,那么是不是因?yàn)閜anic與defer沒有先后關(guān)系呢?我們先來下面的例子:

func main() {
    defer_call()
}

func defer_call() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("one=", err)
        }
    }()
    defer func() { fmt.Println("打印前") }()
    defer func() { fmt.Println("打印中") }()
    defer func() { fmt.Println("打印后") }()

    panic("觸發(fā)異常")

}

大家再多次執(zhí)行恢恼,看看是否都是輸出:

打印后
打印中
打印前
one= 觸發(fā)異常

那為什么沒有加recover()時(shí)候民傻,panic執(zhí)行順序不定呢?
defer的執(zhí)行順序肯定是FILO的场斑,但是沒有被recover的panic協(xié)程(線程)可能爭(zhēng)奪CPU的順序比defer快漓踢,所以造成了這樣的情況,也可能是寫緩存問題和簸,所以對(duì)panic進(jìn)行recover將其加入到defer隊(duì)列中彭雾。

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
    }

}

考點(diǎn):foreach
解答:這樣的寫法初學(xué)者經(jīng)常會(huì)遇到的薯酝,很危險(xiǎn)!
與Java的foreach一樣爽柒,都是使用副本的方式吴菠。所以m[stu.Name]=&stu實(shí)際上一致指向同一個(gè)指針, 最終該指針的值為遍歷的最后一個(gè)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},
    }
    // 錯(cuò)誤寫法
    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)
    }
}

3. 下面的代碼會(huì)輸出什么,并說明原因

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

考點(diǎn):go執(zhí)行的隨機(jī)性和閉包
解答:誰(shuí)也不知道執(zhí)行后打印的順序是什么樣的心墅,所以只能說是隨機(jī)數(shù)字酿矢。
但是A:均為輸出10榨乎,B:從0~9輸出(順序不定)。 第一個(gè)go func中i是外部for的一個(gè)變量瘫筐,地址不變化蜜暑。遍歷完成后,最終i=10策肝。 故go func執(zhí)行時(shí)肛捍,i的值始終是10。

第二個(gè)go func中i是函數(shù)參數(shù)之众,與外部for中的i完全是兩個(gè)變量拙毫。 尾部(i)將發(fā)生值拷貝,go func內(nèi)部指向值拷貝地址棺禾。

4. 下面代碼會(huì)輸出什么缀蹄?

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("teacher showB")
}

func main() {
    t := Teacher{}
    t.ShowA()
}

考點(diǎn):go的組合繼承
解答:這是Golang的組合模式,可以實(shí)現(xiàn)OOP的繼承帘睦。 被組合的類型People所包含的方法雖然升級(jí)成了外部類型Teacher這個(gè)組合類型的方法(一定要是匿名字段)袍患,但它們的方法(ShowA())調(diào)用時(shí)接受者并沒有發(fā)生變化。 此時(shí)People類型并不知道自己會(huì)被什么類型組合竣付,當(dāng)然也就無法調(diào)用方法時(shí)去使用未知的組合者Teacher類型的功能。

showA
showB

5. 下面代碼會(huì)觸發(fā)異常嗎滞欠?請(qǐng)?jiān)敿?xì)說明

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

考點(diǎn):select隨機(jī)性
解答:select會(huì)隨機(jī)選擇一個(gè)可用通用做收發(fā)操作古胆。 所以代碼是有肯觸發(fā)異常,也有可能不會(huì)筛璧。
單個(gè)chan如果無緩沖時(shí)逸绎,將會(huì)阻塞。但結(jié)合 select可以在多個(gè)chan間等待執(zhí)行夭谤。有三點(diǎn)原則:

  • select 中只要有一個(gè)case能return棺牧,則立刻執(zhí)行。
  • 當(dāng)如果同一時(shí)間有多個(gè)case均能return則偽隨機(jī)方式抽取任意一個(gè)執(zhí)行朗儒。
  • 如果沒有一個(gè)case能return則可以執(zhí)行”default”塊颊乘。

6. 下面代碼輸出什么?

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
}

考點(diǎn):defer執(zhí)行順序
解答:
這道題類似第1題 需要注意到defer執(zhí)行順序和值傳遞 index:1肯定是最后執(zhí)行的醉锄,但是index:1的第三個(gè)參數(shù)是一個(gè)函數(shù)乏悄,所以最先被調(diào)用calc(“10”,1,2)==>10,1,2,3 執(zhí)行index:2時(shí),與之前一樣,需要先調(diào)用calc(“20”,0,2)==>20,0,2,2 執(zhí)行到b=1時(shí)候開始調(diào)用恳不,index:2==>calc(“2”,0,2)==>2,0,2,2 最后執(zhí)行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

7. 請(qǐng)寫出以下輸入內(nèi)容

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

考點(diǎn):make默認(rèn)值和append
解答:make初始化是由默認(rèn)值的哦檩小,此處默認(rèn)值為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]

8. 下面的代碼有什么問題?

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
}

考點(diǎn):map線程安全
解答:可能會(huì)出現(xiàn)fatal error: concurrent map read and map write. 修改一下看看效果

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

9. 下面的迭代會(huì)有什么問題?

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
}

考點(diǎn):chan緩存池
解答:看到這道題烟勋,我也在猜想出題者的意圖在哪里规求。 chan?sync.RWMutex?go?chan緩存池?迭代? 所以只能再讀一次題目筐付,就從迭代入手看看。 既然是迭代就會(huì)要求set.s全部可以遍歷一次阻肿。但是chan是為緩存的家妆,那就代表這寫入一次就會(huì)阻塞。 我們把代碼恢復(fù)為可以運(yùn)行的方式冕茅,看看效果

package main

import (
    "sync"
    "fmt"
)

//下面的迭代會(huì)有什么問題伤极?

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

10. 以下代碼能編譯過去嗎姨伤?為什么哨坪?

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

考點(diǎn):golang的方法集
解答:編譯不通過! 做錯(cuò)了UС当编?說明你對(duì)golang的方法集還有一些疑問。
一句話:golang的方法集僅僅影響接口實(shí)現(xiàn)和方法表達(dá)式轉(zhuǎn)化徒溪,與通過實(shí)例或者指針調(diào)用方法無關(guān)忿偷。

11. 以下代碼打印出來什么內(nèi)容,說出為什么臊泌。

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

考點(diǎn):interface內(nèi)部結(jié)構(gòu)
解答:很經(jīng)典的題鲤桥! 這個(gè)考點(diǎn)是很多人忽略的interface內(nèi)部結(jié)構(gòu)。 go中的接口分為兩種一種是空的接口類似這樣:

var in interface{}

另一種如題目:

type People interface {
    Show()
}

他們的底層結(jié)構(gòu)如下:

type eface struct {      //空接口
    _type *_type         //類型信息
    data  unsafe.Pointer //指向數(shù)據(jù)的指針(go語(yǔ)言中特殊的指針類型unsafe.Pointer類似于c語(yǔ)言中的void*)
}
type iface struct {      //帶有方法的接口
    tab  *itab           //存儲(chǔ)type信息還有結(jié)構(gòu)實(shí)現(xiàn)方法的集合
    data unsafe.Pointer  //指向數(shù)據(jù)的指針(go語(yǔ)言中特殊的指針類型unsafe.Pointer類似于c語(yǔ)言中的void*)
}
type _type struct {
    size       uintptr  //類型大小
    ptrdata    uintptr  //前綴持有所有指針的內(nèi)存大小
    hash       uint32   //數(shù)據(jù)hash值
    tflag      tflag
    align      uint8    //對(duì)齊
    fieldalign uint8    //嵌入結(jié)構(gòu)體時(shí)的對(duì)齊
    kind       uint8    //kind 有些枚舉值kind等于0是無效的
    alg        *typeAlg //函數(shù)指針數(shù)組渠概,類型實(shí)現(xiàn)的所有方法
    gcdata    *byte
    str       nameOff
    ptrToThis typeOff
}
type itab struct {
    inter  *interfacetype  //接口類型
    _type  *_type          //結(jié)構(gòu)類型
    link   *itab
    bad    int32
    inhash int32
    fun    [1]uintptr      //可變大小 方法集合
}

可以看出iface比eface 中間多了一層itab結(jié)構(gòu)茶凳。 itab 存儲(chǔ)_type信息和[]fun方法集,從上面的結(jié)構(gòu)我們就可得出播揪,因?yàn)閐ata指向了nil 并不代表interface 是nil贮喧, 所以返回值并不為空,這里的fun(方法集)定義了接口的接收規(guī)則猪狈,在編譯的過程中需要驗(yàn)證是否實(shí)現(xiàn)接口 結(jié)果:

BBBBBBB

來源:https://my.oschina.net/u/553243/blog/1478739

添加小編微信:grey0805箱沦,加入知識(shí)分享小分隊(duì),別掉隊(duì)哦雇庙!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谓形,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子状共,更是在濱河造成了極大的恐慌套耕,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件峡继,死亡現(xiàn)場(chǎng)離奇詭異冯袍,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門康愤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來儡循,“玉大人,你說我怎么就攤上這事征冷≡裣ィ” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵检激,是天一觀的道長(zhǎng)肴捉。 經(jīng)常有香客問我,道長(zhǎng)叔收,這世上最難降的妖魔是什么齿穗? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮饺律,結(jié)果婚禮上窃页,老公的妹妹穿的比我還像新娘。我一直安慰自己复濒,他們只是感情好脖卖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著巧颈,像睡著了一般畦木。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上洛二,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天馋劈,我揣著相機(jī)與錄音,去河邊找鬼晾嘶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛娶吞,可吹牛的內(nèi)容都是我干的垒迂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼妒蛇,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼机断!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起绣夺,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吏奸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后陶耍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奋蔚,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了泊碑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坤按。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖馒过,靈堂內(nèi)的尸體忽然破棺而出臭脓,到底是詐尸還是另有隱情,我是刑警寧澤腹忽,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布来累,位于F島的核電站,受9級(jí)特大地震影響窘奏,放射性物質(zhì)發(fā)生泄漏嘹锁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一蔼夜、第九天 我趴在偏房一處隱蔽的房頂上張望兼耀。 院中可真熱鬧,春花似錦求冷、人聲如沸瘤运。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拯坟。三九已至,卻和暖如春韭山,著一層夾襖步出監(jiān)牢的瞬間郁季,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工钱磅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梦裂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓盖淡,卻偏偏與公主長(zhǎng)得像年柠,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子褪迟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 導(dǎo)語(yǔ) Go語(yǔ)言(也稱為Golang)是google在2009年推出的一種編譯型編程語(yǔ)言冗恨。相對(duì)于大多數(shù)語(yǔ)言,gola...
    star24閱讀 8,995評(píng)論 5 21
  • Golang是我最喜歡的一門語(yǔ)言味赃,它簡(jiǎn)潔掀抹、高效、易學(xué)習(xí)心俗、開發(fā)效率高傲武、還可以編譯成機(jī)器碼… 雖然它一出世,就飽受關(guān)注...
    盤木閱讀 3,553評(píng)論 0 7
  • 一個(gè)人的征程,真的害怕過 騎到烏漆嘛黑的地方谱轨,想到發(fā)生女生被害的無數(shù)案例戒幔,有過原路返回的沖動(dòng),但內(nèi)心那份小堅(jiān)持讓我...
    FYF_2736閱讀 191評(píng)論 0 0
  • 是不是人到了一個(gè)完全不同的環(huán)境之后就會(huì)變的和以前不一樣土童? 剛才想了好久诗茎,但不知道想了些什么。 從一個(gè)花了九年時(shí)間漸...
    吶吶我就是Asa君閱讀 321評(píng)論 0 1
  • 《日精進(jìn)打卡》 姓名:卜令其 公司名稱:寧波大發(fā)化纖有限公司 《幸福精進(jìn)》學(xué)習(xí)營(yíng)第41期努力組 【日精進(jìn)打卡第01...
    _奮斗的破小子_閱讀 217評(píng)論 0 3