前言
本文主要分两部分,前半部分聊了聊这次找工作中我自己的一些想法和体会,因为是随性而发,可能会写得比较啰嗦,第二部分才是实际的面试题记录和我的回答思路,如果只是想了解下面试题的小伙伴直接跳去第二部分开始看吧~
另外面试题中我是有选择性地记录的,记录的标准是,我个人认为问得比较有水平的,我感觉自己没回答好的,或者说我觉得这部分还没形成自己的知识积累,先记下来作为以后的学习参考的。所以里面所写的回答的思路不一定对,而且记录里也没涉及到特别基础的那部分知识,如果是想参考基础部分的知识的话在下更推荐你去 https://github.com/CyC2018/CS-Notes/ 这里去整体过一遍。
那么接下来开始第一部分~
一、对于找工作,个人的一些想法和体会
1. 关于个人的提升与学习,以及学习的针对性
1)需要明确的第一点是,在软件开发这一块,无论想要学习什么,首先你得基础足够好。因为无论学习看起来多么炫酷,高大上的软件技术,其本质上还是离不开两点:①它是以编程语言作为表达载体的;②它是以电子计算机作为运行载体的。明白这两点之后会发现,其实很多东西万变不离其宗,基础足够好的人,学习起来的效率也会比其他硬啃的人高很多。
2)那么问题来了,所谓的基础是指什么?①以一个程序员的角度上来说,首先得对冯·诺依曼提出的计算机体系结构的工作原理(包括CPU,内存,磁盘等,当然也不用很深,毕竟又不是做硬件开发的)有一定的了解,其次要了解计算机网络,包括TCP/IP,UDP,HTTP等知识;②进一步说,从一个JAVA程序员的角度出发,你当然需要对JDK的语法,各项特性,原生提供的各种API都有一定程度的熟悉,同时对JVM内存模型有所了解,然后就是对里面一些经典的数据结构有对其源码有自己的理解和体会,例如最为大家广泛流传的HashMap,里面包含的hash一致性,散列表,链表,平衡二叉树,以及其精妙绝伦的对于位运算的运用,能把它内部学通一遍会有非常大的收获;
3)不少小伙伴会疑惑,这些东西是不是就是为了学来“面试造火箭”的?“上班拧螺丝”的时候真的能用上吗?我想说,真的有用,但我这里不知道该怎么举出例子来说服你,我只能说,当你积累的知识足够多的时候,回过头去和自己曾经的工作经验结合起来去细细体会,你会发现它们其实很有用,但是你积累不够多之前没法发现而已。
4)除了基础需要扎实外,学习还需要有针对性。我们常常说知识之海,你能把整个大海喝下去吗?不能的,精力有限,时间有限。有一种说法叫做T字形的知识结构,就是在学习上有一定的广度,同时每一块也有一定的深度,这个该如何平衡要看个人,也看你的目标。例如有人想去阿里,阿里是做什么的啊?做支付,做金融,那么就要有针对性地了解有什么技术是互联网通用的,同时关注金融,支付相关的特别适用的,契合度高的相关技术。
2. 裸辞还是骑牛找马?
关于这一点的话,每个人情况不一样,我只能结合自己的情况给出一些想法,诸君想如何选择还是看自己。
1) 首先我个人的性格特征是,在某一个方面受到比较大的压力时(这里特指工作)会消耗大量精力,进而导致其他方面的思考能力降低,容易钻牛角尖。所以对我个人来说,每隔一个较长的周期之后,给自己一段相对比较放松的时间去好好整理自己的知识网络,并且思考总结,想明白一些事情,是很必要的(我裸辞后那小半个月的时间想明白的问题比前面半年可能都多),所以对我来说,先辞职,自我整理,然后重新开始下一段工作,是一个比较好的过程。当然这也与我个人的调整能力不高有一定关系,能自我调整压力进行思考的小伙伴可能对于裸辞放松的必要性不大;
2)裸辞之后的心理压力,对于自己的经济储备以及求职信心来说是负相关的。你的经济储备越多,对于找工作的自信越强,你赋闲在家时的压力就相对小;反过来说,经济储备不足,而且对于求职信心不足,裸辞反而会让你个人非常非常非常焦虑,压力会把你压得喘不过气来,这样的话反而得不到放松思考的效果了,还不如骑牛找马,找到下家了再平滑过度;
3) 对于我个人来说还有一点是我是跨城市从广州跳槽上海,虽然现在大部分大公司都支持电话交流,但实际上我个人是觉得还是面对面聊天更靠谱一点,所以我这边除了裸辞之外其实没其他很好的选择。至于跨城市求职的话,如果经济储备还行,而且身上有贵重物品的,不妨找个1~2月的短租房,小猪短租,airbnb都是不错的平台,有个好的地方住也有助于你一边求职一边专心整理,学习。
3. 对于外包公司的一些看法
大多数人不建议年轻人进外包公司,从我个人来说,包括人员外包和项目外包的公司我都做过,可以跟大家聊聊我的看法。
1) 并不是所有的外包公司都是需要看甲方脸色的,这个跟自己同时也跟甲方公司文化有关系。不管是项目外包还是人员外包,作为乙方都需要和甲方对接这个毋庸置疑,有部分甲方公司会把乙方人员看做纯粹是来干苦工的,甚至觉得自己身份比对方高,这种乙方过去了基本上就是受气的,而且可能得不到任何提升,去了就是一片黑暗的未来;
而另一种甲方,除了让乙方干活之外,也会把乙方看做是人才储备,在部门有HC且乙方表现出色的情况下会考虑引荐,至少在我上一家公司,领导(不管是我方还是甲方)其实都挺信任我,给了我很多尝试实践的机会(也因此犯了不少错,让他们帮我背了黑锅,想想还是有点抱歉的),走的时候也是问过我要不要考虑加入他们。所以,虽然我也并不推荐做外包,但如果在出路不多的情况下,也没必要钻牛角尖宁愿饿死也不去外包,至少混口饭吃总是可以的;
2)哪怕不用看脸色,在甲方的地盘上班还是会让你明显感觉出自己是个外人。虽然我上面说了有部分公司(包括大部分知名外企,互联网企业,也包括一些文化相对开放的国企)甲方跟乙方的工作内容和人员处于一个相对平等的地位,但实际上在组织编制上,员工福利上,提升机会上,以及部分核心的保密的工作内容上,你还是会在里面处处感觉自己像个外人,时间久了会越做心里越委屈(真的就是觉得委屈,不是矫情),而且想要转正也是需要等对方的HC,哪怕有了HC,人家也可能会更愿意拿来招211985的应届大学生而不是引荐你转正。
所以其实说句比较尖锐的话就是,公司之所以招乙方,其实更多的是因为乙方人员好打发,转正不是完全没机会,但其实有限。至少我个人目前是不想再去外包公司了,除非是已经没有出路的情况下。
4. 写简历需要注意的事情
简历上尽量要求真务实,有自知之明,并且自信,自己会什么就写什么,对于自己擅长的点可以稍加笔墨详细点去描述,而对于自己只懂点皮毛的点则慎用熟悉,精通等词,更建议用了解这个词(见过有人用会用XXX这种描述写到简历上的,我只能说emmmm真睿智),因为你简历如果写得太浮夸,自己明明只懂点皮毛偏要写个精通上去,最后面试的时候答不出题会异常尴尬,而且面试官对你的印象也会很差,觉得你在造假。
5. 面试时的沟通技巧,怎么样给面试官留下好印象
1) 面试的时候其实也跟写简历类似,需要你有自知之明,并且自信。①如果面试官提出了你不熟悉的问题,直白点说出自己知道的东西,剩下的就直接说不知道或者不清楚,然后也可以主动提出说其实你的精力集中在了某方面,所以对当前问的这个问题理解不深,这样来引导面试官去了解你擅长的方面;②如果面试官提出的问题刚好是你熟悉的,下过苦工去钻研的,尽量多说点,保持逻辑清晰,思路清楚,并且加入自己的理解最好,就我目前发现来说,面试官对于能说清楚知识点之余同时加入自己的思考和理解的人会很欣赏;
2) 说话的时候有礼貌,条理清晰就好,其他的什么技巧其实也是细枝末节。很多时候其实面试也讲究一个相性的问题,你跟面试官的相性高,那很容易可以跟对方谈笑风生;但大多数情况下两个陌生人见面聊天也就是一个你问我答的过程,有基本的礼貌,回答问题逻辑清晰,保持跟对方的交流而不是你自己自顾自地说就好,其他的什么乱七八糟的什么微表情什么心理学的技巧都是画蛇添足了,你是去面试当程序员而不是心理医生;
2) 面试的时候要心态好,不要觉得面试官身份比你高因而觉得紧张甚至害怕,面试官更愿意看到一个自信的,思维清晰的人。要知道面试是一个双方互相选择互相交流的过程,你跟面试官在这个过程中是平等的,大家都是人,面试官可能无非就是技术比你好,收入比你高,难道他因为比你强一点就不是人而是神了吗?
3) 面试除了你回答面试官的问题之外,其实也可以向他请教你的疑惑。前面已经说了,面试是一个双方互相平等交流,选择的过程,有素质的面试官,往往会在对你的提问结束后给你时间去提问,这个时候除了关于公司的问题之外,其实也可以提出你自己私人上一些疑惑和问题,实际上说不定还可以得到加分,因为,你会产生疑惑,说明你是真的下了不少心思去钻研它,而前面也提过,部分面试官会非常欣赏有独立思考能力的人。
以我个人举例子的话,提问环节我会喜欢先提问公司的营业模式,技术栈,开发时会遇到的一些挑战和难题,最后提出自己最近的一些疑惑,例如这阵子的面试,我都会提出【对于学习的深度与广度上的取舍与平衡,您个人有经验可以分享下吗?】以及【如果自己学习到的新知识无法在公司的实际项目中得到实践,如何自己创造机会去做实践呢?】这两个问题,前辈们也都很友善地给我分享了他们的想法,也不得不说每个人看问题的角度不一样,综合他们的经验下也让我得到了不少收获。
那么我个人的总结到这里结束了,下面就是我整理的印象比较深的面试题。
二、面试记录
20190509 ebay初面:
一)开发计划中的角色,职责,代码主要写的什么内容,涉及到什么逻辑之类的
1. 参与业务分析与开发方案的设计,从业务需求转换到具体的web api接口
2. 开发方面主要涉及到
- 微服务接口开发,一般的逻辑包括:
1.) 入参的必填项,或者有条件的必填项
2.) 贴合业务逻辑的代码编写,基本上就是CRUD,有可能根据需要外呼其他接口
3.) 若发生异常(程序异常,业务异常)根据不同异常情况返回提示信息
4.) 成功则根据消息格式返回 - 定时任务的开发,一般逻辑为:
1.) 检查当前异步程序的执行要素是否齐全,不齐全则写日志,直接退出
2.) 根据执行要素开始程序,任务多为IO密集型,会开多线程
3.) 多线程,为了保证一致性,一般用
i.生产者-消费者模式,在主线程将数据进行分割,避免冲突
ii.无法避免冲突的情况下,使用锁,锁的粒度尽量小,以减少锁占用时间
二)接口设计的个人体会和经验
- 考虑接口的出参和入参格式
- 考虑异常情况及异常发生时的提示
- 接口的扩展性,业务变更可能性
- 代码追求简洁高效,SQL同理
20190510 平安壹钱包
一)简单算法:
如何翻转字符串:
当时的回答:转成char数组,循环长度的一半,头尾交换值;
回答的算法是正确的,但可能说得太简练,而且也是最常见的方法,对方反馈一般般,下次算法题考虑说详细点
二)zookeeper的使用和理解,以及原理
这里简单地答了应用,和服务发现,注册,心跳,负载均衡,和ZAB一致性协议,但因为原理研究不深,应用也没答全(没有说分布式锁,也没说到集群管理之类的),反馈一般
考虑找时间详细地补一下zookeeper的原理
同理也应该详细补一下kafka的原理,甚至源码
redis只是简单问了应用,使用场景,但后续估计会问到工作原理,有时间也需要准备一下
spring原理现在感觉都问烂了,面试官都懒得问了,我还特地花了那么多心思去钻呢(残念脸
三)有部分回答可能会显得有些啰嗦,例如问到沟通,问到团队合作,之类的,应尽量简洁高效地回答对方自己的处事方式和工作沟通
四)说出自己比较做得比较好的项目和理解,这一块前面答得略微简单,可以考虑以下回答:
简单回答最近在CCB做的这个项目就是最有挑战性的,因为前面都是单体式应用,这是第一次接触分布式,并发量可能高达几千上万,同时数据量也达到了千万级甚至亿级,对开发工作的要求严格很多。我对于这个项目的研究和学习里对与分布式与微服务得到了不少的理解,同时也在这个项目中学习到了各种比较流行的,包括kafka,redis等流行的中间件应用,同时也会面对更复杂的业务场景,使我对于业务和技术结合有了更深的理解。
我在这个项目中负责相关的业务分析和开发方案的设计,开发日程的讨论,以及开发微服务接口和异步程序,除此之外也负责对新同事进行基础的技术和业务培训,给他们的开发工作进行指导
20190513 eBay二面:
一) 第一个面试官,感觉考察的是架构能力,问题的分析和追踪能力,同时还顺便考察了知识面的广度
一上来就先画了一下他们某个电商微服务的架构图让我分析问题,记录如下:
(我就随便跟着印象瞎画一下,我知道很糟糕,希望未来的我看到了不要回来打现在的我)
当时初步提供的信息里没提到持久层MQ,后来我问出来的。
提出的问题是:两个请求A和B,A将value从F更新成T,B从T更新成F,执行结束后在DB查到的结果是T,但是log console里可以查到有A和B完整执行的记录,请查找原因
在一步步的询问查找,包括对框架的分析中可以知道,由于在log console中可以找到执行日志,且前面从前端到scheduler到worker集群都是保证顺序的,所以通过log console的结果可以看出问题并不在前面的步骤。
最后得出的结论是在持久层API内的问题,因为MQ并不是直接操作到持久层的,而是发送请求到持久层,由持久层API进行操作,我一开始没问清楚,以为持久层是直接操作。
至于为什么会出问题,因为持久层的API也是集群(很鸡贼,我前面问的时候告诉我持久层可以看做是一个单体API,但是后来挑明之后又告诉我看做是单体API是从外部的角度来看,只向外部暴露了API,外部不需要知道内部细节,但内部实际上是一套持久层API的集群),在集群中,可能A和B被分配到了不同的持久层节点,虽然MQ内是有顺序的,但是可能A在执行过程中节点被阻塞,导致B的操作先完成了,最后导致A后更新。
解决方法:一开始考虑了在持久层内部使用raft算法或者别的选举算法来定位一个Leader,由leader来保证顺序,但是这样对吞吐量不利,实际上更多地应该考虑使用乐观锁可以解决问题。
二) 第二个面试官,感觉侧重在对以前项目的理解深度,包括业务,架构和开发模式等等,顺便也考察了面试者有没有主动去对框架和架构进行了解学习的积极性?另一方面就是重点考察基础知识,特别是多线程
写一个死锁程序:
妈蛋当时太紧张忘了,后来面试完在路上突然间又想起来了,但是也是因为对死锁理解不够深,多线程还要继续补补,而且记住以后面试千万不要紧张kafka(MQ)的分区,扩展怎么扩,根据什么来扩容?
一开始以为这里问的是业务场景,但是回头想想应该问的是kafka的分区原理说一下线程同步,如何保证?
对于线程同步的理解不够清晰没答好,后续补
回来查了一些资料之后我认为相对靠谱的答案:线程同步,从内存的角度上来说,在多线程环境下,对同一个内存地址进行操作,同一时间内只能有一个线程进行操作,其他线程必须要等待;从程序的角度上看的话,就是多线程环境下,多个线程对同一个公共资源进行操作时,这个资源的状态的变化在所有线程角度上观察是一致且有序的。
要保证线程同步,那就要保证每次对公共资源进行写操作的时候,只能有一个线程参与,其他线程只能等待。想要做到这一点,可以通过synchronized关键字或者lock接口对资源进行加锁
除了对资源进行加锁之外还可以对方法,或者片面的代码块进行加锁,如此在执行相关的代码时就只能是单个线程参与。JVM如果发生OOM如何查问题
基本思路是对的,查看最近的接口是否有长时间存活的大对象,是否对大文件读写没注意,查看是否集合类被一直引用;
跟踪GC情况,用jstat –gcutil
生成堆快照,jmap
分析堆快照,jhat
还可以用包括eclipse等ide进行分析问到了项目间用什么协议通讯
因为紧张把网络协议的知识都忘了,没答好,说了TCP(扶额),实际上应该是HTTP,内部用流进行传输
三) 第三个面试官跟第二个差不多,也是重点考察基础,基础进阶,包括多线程和JVM等,同时还考察了方案设计能力
聊聊JVM的内存结构,顺便说说GC
当时把VM栈和本地方法栈的作用忘了,其他答得还行
JVM栈记录的是JAVA方法执行的内存模型,存储了包括局部变量表,操作数栈,方法出口等等信息,
本地方法栈同理,不过记录的是native方法的信息讲讲简历中提到的,开发的定时任务,异步任务之类的思路
当时常规地讲到了大部分是IO密集型的数据处理任务,然后跟他聊了聊常用的生产者-消费者模式
详细地问完,反问我,如果生产者对于数据的生产速度较慢,需要也用多线程生产怎么办
生产者线程组跟消费者生产组分隔开,中间建立一个调度线程,调度线程负责对生产者的发送的数据作为一个缓冲,缓冲池有个最大值,当生产者检查到缓冲池满了,则休眠,调度线程会唤醒消费线程;同理,消费线程不运作的时候会休眠索引
20190513 蚂蚁金服初面,电面
一) 分布式事务
分布式事务,首先可以围绕着CAP定理开始聊
聊完之后考虑实现方式,包括2PC(二阶段提交)和TCC(补偿事务)
也可以异步MQ记账,最后通过日终核算以及人工补偿达到最终一致性
二)问了排序算法,回答了冒泡排序和快速排序
后来被追问时间复杂度,忘记怎么算了,扑街
20190515 花旗银行
一)关于线程池,JDK原生提供的几种线程池,自己创建线程池的话对于参数设置的心得
例如说某块功能需要用线程池做处理,有峰值低估,该怎么设置?
原生提供的线程池包括single(单线程),fixed(固定长度),cache(可缓存可回收利用),scheduled(定长,支持周期性或定时任务,用schedule方法而不是execute来执行)
corePoolSize核心线程数(当忙碌线程数小于核心线程数时,会将已有的空闲线程回收再用,节省资源),maximumPoolSize最大线程数,线程池中允许线程数最大值
keepAliveTime 空闲线程的最大存活时间
workQueue 提供存放待运行的任务队列的队列类型,有多种策略
RejectedExecutionHandler 拒绝策略接口
二)关于synchronized
部分基础细节记不清楚,回答的时候很模糊,记录如下:
- 将synchronized加到方法定义上,起什么作用,分两种情况,
- 如果是一般方法,则相当于将当前对象加锁,例如对象A,有method1和method2加了synchronized,那么当线程T1调用A.method1,线程T2调用method2,由于是对A对象实例加锁,所以会根据加锁顺序,先执行T1,再执行T2
- 但是如果T1和T2调用的是不一样的对象,例如T1调用A.method1,T2调用B.method2,那么两个线程之间加锁对象不一样,没有顺序,但有一种情况,如果method1和method2调用了静态变量,那么会产生对静态变量的竞争,会自动对静态变量加锁,所以也会有顺序
- 如果是synchronized加到静态方法上的话,就是对类进行加锁,而不是对对象实例进行加锁,或者说是对类A的class对象加锁
三)算法题问到的是一个动态规划的应用场景,但是相对简单,用普通的递归也可以做
这里回答得不太好,没有一开始想到用动态规划,稍微需要对算法也补一下
20190517 壹沓科技(爬虫,大数据,机器学习)
一)问到项目内相关技术选型的经验,判断标准,并举出具体例子
- 选型,根据场景考虑需要用什么,然后对这个工具进行初步的了解,学会它是怎么用的,怎么样可以解决目前的问题,最后将其与其他类似或相同功能的东西进行对比,判断我为什么要用它,同时也参考网络上其他人的使用经验等,最后自己进行参考,并且进行实际的测试
- 例如说kafka,我个人目前对它理解里比较看重的两点是,
- 它配合zk一起使用,可以对分区进行灵活的扩展,有助于提高吞吐量,而且使用简单;这一块是对应我们的业务场景之余也是考虑到我们公司的背景,我们那边其实也是为了这块的功能,第一次引入使用MQ,当时考虑的是,选一款简单易上手的MQ进行使用,甚至可能组内别的功能会直接用我这边搭建完成的现成的东西,也想选一个对于分区上方便扩展的东西来用,所以同时也是刚好我个人在学习kafka mq,我就自告奋勇的接下来了来做这个东西;
- 关于第二点的话,我这边了解到kafka对比其他的MQ,在实时性上的性能是比较逊色的,但是它有一个很好的特性就是它会使用到硬盘的log文件进行一个消息的持久化,我们其实也是看重了这一点,我们那边是银行机构嘛,可能更倾向于使用容灾性强的,并且恢复简单的工具,同时也想选用一个使用简单方便培训的工具,后续扩展,推广可以开箱即用,kafka在这一点上也很符合我们的期望;
- 至于第三点就会比较简单了,就是我个人在过程中也根据我们的业务场景去进行了尝试,当时上游接口告诉我们说会有每秒5~7千,峰值可能会到达上万的消息传输量,而我这边进行过自己的实测,kafka哪怕是单分区情况下,每秒钟进行2w的生产和消费是完全没有性能瓶颈的,这里已经是达到了我们业务预测值的两倍了,所以也会认为它是符合我们的期望
就经过上述几点的考虑的话,我们这边引入了kafka作为我们对于MQ的第一次尝试
20190521 拉卡拉(支付)
一)分布式架构中该如何拆分微服务?可以按些什么标准,有些什么方法论?
当时回答的是可以按业务拆分(这也是最常见的一种方式),至于如何确定拆分粒度,当时提出了一些《聊聊架构》中的一些思想,包括核心生命周期与非核心生命周期。但因为这两块学得还不够透彻,没有形成足够的知识积累,后面再深入学习架构方面的知识。
二)还是问到了分布式事务的基础。同时问,如果一个分布式事务,涉及到一个很长的调用链,而且横跨多个模块,该如何设计它来尽量提高容错率和正确率?直接设计一个可行的方案。
这里其实用不着什么2PC和TCC了,根本不实用,其实大型的分布式事务可以考虑使用异步MQ进行调用链的合并,最后用日终对账/核算来处理异常情况。
三)还是谈到了中间件的选型,谈到了kafka看中了它什么特性,回答说了前面提出过的回答,被追问,其他MQ也有持久化的策略,而且也有入门难度低且速度快的,有没有仔细对比过?
这一块没答出来,后续考虑对技术选型方面的考虑和知识考虑更深点。