面试找工作,一定要做好充足准备,不仅技术要学的扎实,还要懂得表达。接下来,老男孩教育为大家总结了几道GO语言面试题及答案,希望对你们有用。

  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("触发异常")
}   

  答:

  输出内容为:

打印后
打印中
打印前
panic: 触发异常

  解析:

  考察对defer的理解,defer函数属延迟执行,延迟到调用者函数执行 return 命令前被执行。多个defer之间按LIFO先进后出顺序执行。

  故考题中,在Panic触发时结束函数运行,在return前先依次打印:打印后、打印中、打印前 。最后由runtime运行时抛出打印panic异常信息。

  需要注意的是,函数的return value 不是原子操作.而是在编译器中分解为两部分:返回值赋值 和 return 。而defer刚好被插入到末尾的return前执行。故可以在derfer函数中修改返回值。如下示例:

package main

import (
    "fmt"
)

func main() {
    fmt.Println(doubleScore(0))    //0
    fmt.Println(doubleScore(20.0)) //40
    fmt.Println(doubleScore(50.0)) //50
}
func doubleScore(source float32) (score float32) {
    defer func() {
        if score < 1 || score >= 100 {
            //将影响返回值
            score = source
        }
    }()
    score = source * 2
    return
    //或者
    //return source * 2
}

  运行结果:

  0 40 50

  该实例可以在defer中修改返回值score的值。

  2、以下代码有什么问题,说明原因

package main

import (
    "fmt"
)

type student struct {
    Name string
    Age  int
}

func pase_student() map[string]*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
    }
    return m
}
func main() {
    students := pase_student()
    for k, v := range students {
        fmt.Printf("key=%s,value=%v \n", k, v)
    }
}

  运行结果:

key=zhou,value=&{wang 22} 
key=li,value=&{wang 22} 
key=wang,value=&{wang 22}

  答:

  输出的均是相同的值:&{wang 22}

  解析:

  因为for遍历时,变量stu指针不变,每次遍历仅进行struct值拷贝,故m[stu.Name]=&stu实际上一致指向同一个指针,最终该指针的值为遍历的最后一个struct的值拷贝。形同如下代码:

var stu student
for _, stu = range stus {
    m[stu.Name] = &stu
}

  修正方案,取数组中原始值的指针:

for i, _ := range stus {
    stu := stus[i]
    m[stu.Name] = &stu
}

  3、下面的代码会输出什么,并说明原因

package main

import (
    "fmt"
    "runtime"
    "sync"
)

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

  运行结果:

i:  9
i:  10
i:  10
i:  10
i:  10
i:  10
i:  10
i:  10
i:  10
i:  10
i:  10
i:  0
i:  1
i:  2
i:  3
i:  4
i:  5
i:  6
i:  7
i:  8

  答:

  将随机输出数字,但前面一个循环中并不会输出所有值。

  解析:

  实际上第一行是否设置CPU为1都不会影响后续代码。

  2017年7月25日:将GOMAXPROCS设置为1,将影响goroutine的并发,后续代码中的go func()相当于串行执行。

  两个for循环内部go func 调用参数i的方式是不同的,导致结果完全不同。这也是新手容易遇到的坑。

  第一个go func中i是外部for的一个变量,地址不变化。遍历完成后,最终i=10。故go func执行时,i的值始终是10(10次遍历很快完成)。

  第二个go func中i是函数参数,与外部for中的i完全是两个变量。尾部(i)将发生值拷贝,go func内部指向值拷贝地址。

  4、下面代码会输出什么?

package main

import (
    "fmt"
)

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

  答:

  运行结果:

showA
showB

  解析:

  Go中没有继承! 没有继承!没有继承!是叫组合!组合!组合!

  这里People是匿名组合People。被组合的类型People所包含的方法虽然升级成了外部类型Teacher这个组合类型的方法,但他们的方法(ShowA())调用时接受者并没有发生变化。

  这里仍然是People。毕竟这个People类型并不知道自己会被什么类型组合,当然也就无法调用方法时去使用未知的组合者Teacher类型的功能。

  因此这里执行t.ShowA()时,在执行ShowB()时该函数的接受者是People,而非Teacher。

  5、下面代码会触发异常吗?请详细说明

package main

import (
    "fmt"
    "runtime"
)

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

  在线运行

  答: 有可能触发异常,是随机事件。

  解析:

  单个chan如果无缓冲时,将会阻塞。但结合 select可以在多个chan间等待执行。有三点原则:

  select 中只要有一个case能return,则立刻执行。

  当如果同一时间有多个case均能return则伪随机方式抽取任意一个执行。

  如果没有一个case能return则可以执行”default”块。

  此考题中的两个case中的两个chan均能return,则会随机执行某个case块。故在执行程序时,有可能执行第二个case,触发异常。

  老男孩GO语言学习周期是多久?我校以周末班、网络班为主,学习周期4-6个月。

  推荐阅读: