求职 记一次面试经历

wanna · 2020年09月04日 · 最后由 Haiyung 回复于 2020年09月27日 · 1843 次阅读
本帖已被设为精华帖!

面试概况

  1. 岗位:golang 开发
  2. 公司:*** (公司名字就匿了,避免看了题再去面试对其他小伙伴不公平)
  3. 途经:朋友内推
  4. 方向:IoT(物联网)/区块链
  5. 用时 2 个多小时(其中半个小时在答题)
  6. 结果:😭 😭
    虽然失败了但是还是有很多值得参考的地方,我还是挺看好这个公司发展的方向的,希望对想到这个(类)公司面试的小伙伴有帮助

面试题目

共 10 题,答题时间 30min,其实这些题都很基础,答案我放题后面

不定项选择题

具体选项不完全确定是这些,但是考察的大概就这些内容

  1. 对于func add(args ...int) int {}调用方式正确的选项有()
    A. add(1, 2)
    B. add(1, 3, 7)
    C. add([] int{1, 2})
    D. add([] int{1, 3, 7}...)
  2. 变量的初始化,下面正确的使用方式是()
    A. var i int = 10
    B. var i = 10
    C. i := 10
    D. i = 10
  3. golang 中的引用类型包括()
    A. string
    B. map
    C. channel
    D. interface
  4. 关于整型切片的初始化,下面正确的是()
    A. s := make([] int)
    B. s := make([] int, 0)
    C. s := make([] int, 5, 10)
    D. s := [] int{1, 2, 3, 4, 5}
  5. 关于 channel,下面语法正确的是()
    A. var ch chan int
    B. ch := make(chan int)
    C. <- ch
    D. ch <-
  6. 关于无缓冲和有缓冲的 channel,下面说法正确的是()
    A.无缓冲的 channel 是默认的缓冲为 1 的 channel
    B.无缓冲的 channel 和有缓冲的 channel 都是同步的
    C.无缓冲的 channel 和有缓冲的 channel 都是非同步的
    D.无缓冲的 channel 是同步的,而有缓冲的 channel 是非同步的

不定项选择题答案 以下答案仅供参考,主程没给我最终答案,上面部分问题是我在网上搜索 golang 面试题后才回想起来的
只要好好准备,这些题都简单,全是基础

  1. ABD
  2. ABC
  3. BCD
  4. BCD
  5. ABC
  6. D

解答题

不管是选择题还是解答题都做到过类似的题型,所以多刷面试题和 leetcode 没毛病
算法题给我出的都是 leetcode 里面的简单难度的,以至于我都觉得不算算法题了~
除了算法还考了点基础概念

  1. 简要描述下变量逃逸
  2. 简要描述下 slice 在 append 时发生了什么
  3. 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。leetcode 链接
  4. 给定两个数组,编写一个函数来计算它们的交集、以及差集(交集简单,重点在差集上)。leetcode 上只讲了交集没讲差集
    如输入 nums1 = [4,9,5], nums2 = [7,5,10,8,4] 交集返回 [4,5] 差集返回 [9],[7,10,8]

变量逃逸这题我懵了,我没看过这个的定义,所以就乱写了一堆

  1. 答:不知此题是否想考 golang 的垃圾回收机制,golang 的垃圾回收机制使用的是三色标记法,即先标记后删除,具体逻辑可在面试时详讲(解释起来有点长太长,所以就写了的面试时在直接给面试官讲...) 但是,实际情况是这样的吗? 并不是!!!下面会有解释

  2. 答: 先看原来的 cap 够不够装下需要添加进去的元素,如果够,则直接添加 如果不够,则判断现在 slice 的大小,大于 1024 则扩容 1.25 倍,不够再扩 1.25 倍;如果小于 1024,则扩容到原来的两倍,同理,不够再扩 2 倍 emmmm,这道题感觉回答得没啥问题,毕竟面试前恶补过 下面为官方实际扩容的规则代码

    func grow(s Value, extra int) (Value, int, int) {
    i0 := s.Len()
    i1 := i0 + extra
    if i1 < i0 {
        panic("reflect.Append: slice overflow")
    }
    m := s.Cap()
    if i1 <= m {
        return s.Slice(0, i1), i0, i1
    }
    if m == 0 {
        m = extra
    } else {
        for m < i1 {
            if i0 < 1024 {
                m += m
            } else {
                m += m / 4
            }
        }
    }
    t := MakeSlice(s.Type(), i1, m)
    Copy(t, s)
    return t, i0, i1
    }
    

后面两道题都是 leetcode 的题,我就不详细解释了
不过我会在下面的主程面试中讲下我是怎么做的,以及怎么 ‘忽悠’ 面试官,让他知道我能做出更好的解决方案的

做完题我感觉我有了,我在考虑要 15K?16K?18K?了

主程面试

由于是内推(在此感谢 @ 刘水水同学的推荐),直接跳过了前面的步骤,做完题面主程了(好像也没跳过啥~~,反正就是给了机会)

面试题讲解

主程先给我讲了下多选题的第三题,我选反了...给说了下引用类型的概念,我还在第三题下面备注了下
抱歉,对于引用类型的概念我不熟悉,根据题意我猜测答案要么是 A 要么是 BCD(即反过来)
结果我还是选反了~不过面试官还是比较满意的我觉得这里也很重要,不要不会就直接放弃了,总要给出自己的结论,并且解释下为什么我这样想备注里面我没写我为什么这样猜测,不过面试是我给面试官解释了下比如 chan 不能单独作为类型,只能是 chan int,chan string 等 map 和 interface 同样的道理(我都想到这里了居然还能答反,我也是醉了)

简答题 1. 面试官给我解释了下变量逃逸的概念:变量逃逸就是变量的作用域的改变
面试官说到这里的时候,我接过来解释到:就是变量从栈逃逸到堆上面去了吗?
得到肯定的回答后,我继续说(为了表明其实我还是理解,只是专业名词不熟悉而已,所以该接过来说的,必须主动接过来说),从栈到堆后会影响运行速度什么的~因为堆上的变量需要 GC 来处理(顺便就吧我上面答的三色原理提了下,表明虽然我的答案有问题,但是也不是毫无关联的~~哈哈哈,机智如我,我觉得此处很重要,即使题没答对至少要让面试官知道我是知道些东西的
Go 变量逃逸分析具体可以参考这篇博文

算法题 1 我用的是两次 for 循环遍历,在面试官评价之前我就先解释了下,一共半个小时,外加手写代码,没办法去考虑更优解,然后就讲了用 map 来解的更优方案(假装完美混过去)
实际上是:面试官问我,你为什么第一眼没想到用 map 呢?
emmmm 好的我错了(不过至少让面试官知道我知道更好的解决方案),实际上两次 for 的做法,在 leetcode 上都能得到双 90 的得分了~

算法题 2,交集我的做法是把长 slice 放 map 里面,然后循环短的 slice 判定另外一个的 map 里面是否含有,若有,则把对应项 append 到结果集中(其实到这里我就反应过来上一题该用 map 了,但是...答题卡位置有限加上时间限制...我就放弃了修改)
差集同样的用 map 加上 slice 的循环找出结果,主程给我说,此处更好的解决方案是使用 map 自带的 delete 方法,把交集删除后,剩下的就是差集了,可以把你们的答案留在评论区。。。。
emmmm 的确更好哈,可惜我没想到
ok 面试题就到这里了,接下来是考了些 go 的基础

go 基础的考核

我尽量回忆当时主程问了的问题,不确定能回忆完 后面的问答题稍微要比前面的题难一点点,不过也没啥太难的 下面只做列举,让同行知道下面试官关心哪些问题

  1. 简单谈下 defer 的应用场景及注意事项(关闭文件网络请求等,先进后出)
  2. 简单谈下 chan 的应用场景及注意事项
  3. 简述进程、线程、协程的含义及区别
  4. 往一个对象里写入 10w 条数据,怎样保证数据的准确性(chan、mutex 之类的胡扯就对了)
  5. 闭包的注意事项(这里变量的引用是个坑)
  6. 简单介绍下 interface 的应用场景(我给扯出什么 2.0go 打算加入泛型什么的,实际上面试官想问的是接口不是 interface{}这个数据类型...,等面试官给我纠正了下思路然后,我又继续讲接口相关的东西...) ... ... ok golang 的基础考核完了,我感觉我目前问题不大基本能过面试这关了
    继续考虑我的 15K?16K?18K?了~~~

和公司业务相关的技术考核

emmmm,然后我就挂在了这里了
由于他们公司是做 IoT 的所以偏向底层
所以就考了我一些操作系统原理的问题
作为一个非科班出生的人,我当场懵逼
我知道接下来我将面临的问题是我无法正确解答的
不过抱着试一试的心态继续淦 以下答案请勿参考,我是乱答的,你只需要看题就够了

  1. 线上 cpu 和内存突然飙高后应该怎么排错(debug)

    emmmm,linux 应该有命令能直接查看哪个进程导致的,然后.... 好吧我不知道

  2. 哪些操作会导致内存泄漏

    有大量的 io 的情况(我也不知道对不对,后来去搜了下,好像是没 close 才会出现)

  3. 哪些操作会导致 io 开销大幅上升

    大量的文件读写操作会导致 io 开销剧增,所以可以用 bufio 缓冲 还有呢? emmm 比如数据库的大量读写 还有呢? emmm 比如缓存的读写... 还有呢? (好吧我错了)不知道了 网络请求也会造成大量的 io,严格意义上来讲数据库的读写也属于网络请求中的一种 emmmm (好的,我明白了,实际上第一次答的时候去掉'文件'这两个字就好了)

...后面还问了些比较涉及操作系统底层的东西,由于我不太能理解所以就记得不太清除了

面试官结语

面:你 golang 的基础不错,语言上没有什么问题,但是我们公司做的 IoT 方向,除了语言本身,还要懂的操作系统原理才行,比如你的算法题,你就没去考虑时间空间的问题,如果你去一个偏向业务的岗位问题不大,但是我们公司是偏向底层的,所以不太适合你 我:那贵公司是否有其他偏向业务开发的部门呢? 面:公司还有个部门是做区块链的,更偏向底层 emmmmm

面试官走了 hrbp 来了(其实就已经结束了)

如果我们公司有其他岗位适合你的,我们这边会通知你的 (哦,理解了,大概意思就是你现在不适合我们公司,面试结束)

个人觉得重要的中间细节

  1. 简答题最好一开始就写最好的方案
  2. 有的东西不知道没关系,但是不回答那你肯定就没戏了,胡扯也得扯,反正你跑远了,面试官会把你拉回来的(有的人觉得,懂就是懂,不懂就是不懂,不要在面试官面前胡扯,面试官都能看出来的,其实我不这样认为,胡扯即使说错了至少能让面试官看到我在其他哪个哪个方面也还是略懂的)
  3. 虽然打断面试官的话是不礼貌的,但是你应该酌情考虑在关键地方合适的 ‘打断’ 面试官的发言,然后你接上往后说,让面试官知道你是知道这个知识点的(这个地方一定要把握好度,没把握好的话,你之前回答的再好你都无了)
  4. 明确你面试的公司的 ‘属性’ 针对性的复习下(但凡我提前看过操作系统原理,我都不会挂得那么毫无悬念)
  5. 如果你有不错开源项目(或者参与的开源项目)一定要写在简历里,如果不是我简历 里面写了和开源相关的东西,我可能连面试的机会都没有,如果有合适的工作岗位,欢迎小伙伴们给推荐哦,我的简历 在 github 上有一份~~

讲道理
我已经很满意我面试的表现了
虽然挂了,但是我把我知道的都说出来了(当然 grpc,http2 那些没考)
而且通过面试官对我的评价以及答题情况让我对自己在 go 语言基础操作上更有自信了
然后就是,一个人、一本书、一个月还你一个奇迹(操作系统原理从入门到放弃)
一年前的我:php 是世界上最好的语言😎
现在的我:go 是世界上最好的语言🚀

最后
祝我自己今年能成功转入 go 开发者的行列中

最后的最后
祝 go 红红火火

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio
kevin 将本帖设为了精华贴 09月04日 09:31

详实而真实的过程

选择题 第三题答案不对 是ABCD 
选择题 第六题题目不对 
        1 不叫同步或异步, 应该说阻塞或非阻塞
        2. chanel 阻塞或非阻塞 不取决于有没有缓冲 而是取决于你怎么用
runner-mei 回复

3 题里面
int、float、bool 和 string 这些类型都属于值类型

6 题里面
有缓冲的 chan 可以先放一堆数据,等缓冲区满了再取来用所以可以理解为异步消费 而无缓冲的必须在推入数据后立刻消费掉,所以可以理解为同步


3 
https://cn.bing.com/search?q=%E5%80%BC%E7%B1%BB%E5%9E%8B+&qs=n&form=QBRE&sp=-1&pq=%E5%80%BC%E7%B1%BB%E5%9E%8B+&sc=8-4&sk=&cvid=3B21CD6B944A472FAA9397DE544AFA75
总结下来不同语言中至少有两点是相同的

1. 值是栈上分配的 引用是堆上分配的
2. 赋值时值是完全拷贝的 引用是部分拷贝的

6 题里面
https://cn.bing.com/search?form=MOZLBR&pc=MOZI&q=%E5%90%8C%E6%AD%A5%E5%BC%82%E6%AD%A5
https://www.jianshu.com/p/aed6067eeac9

这个有点绕你可以这么理解

你可以用 chan 来做异步但你不能说 chan 是异步的  chan 来说 
你调用 chan <-  <- chan  chan 来说调用已经结束了本次发
送或接收任务已经完成或失败

你不能将你上层业务的行为作为 chan 的行为 chan 是你实现上层业务
异步行为的组件但你不能说这个 chan 组件是异步的

我再举一个例子现在的数据库接口都是同步的或阻塞的 golang 
数据库接口可以认为是异步的 java  jdbc 肯定不是)。你可以用它
来实现异步调用将一张表作为队列插入记录为入队队取记录为出队列
这时你说数据库接口是同步还是异步

runner-mei 回复

针对通道是否有同步异步的注意:

  1. 元素值被发送进入通道,不是元素本身,而是其复制值;

  2. 从通道接收元素值,接收到的也不是元素本身而是其复制值;

  3. 非缓冲通道由于没有缓冲,所有针对非缓冲通道做的发送和接收做都会阻塞,只有收发双方对接上了,执行才会继续,而且非缓冲通道是是直接从发送方复制到接收方,因此,非缓冲通道是用同步的方式传递数据;

  4. 对于缓冲通道,发送操作会先将元素复制到缓冲通道,接收操作也是从缓冲通道内复制值出来,缓冲通道的空间决定了是否阻塞,因此,缓冲通道的确是在用异步的方式传递数据;

--可参考极客时间《Go 语言核心 36 讲之通道的基本操作》https://time.geekbang.org/column/article/14660

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册