终于等到心心念念的offer
平时自己有经常看牛客,也看了快一年的面试经验,帮助真的很大,所以还是来分享一点自己这学期以来的面试感想。本人来自非211/985的学校,主要学习的Java,投的简历也都是后台开发。因为没有优质项目,投错成17届应届生,笔试挂等原因没有各位大佬那么多的面试经验。目前为止只有面试三家公司,阿里菜鸟内推(HR挂),腾讯(offer),京东(offer),也打算结束春招了。为了高度还原案发现场,以下回答都为当时回答内容记录,难免有很多考虑不周和错误,还请大家注意和见谅。
阿里电话一面, 1 个小时 5 分钟
问:自我介绍。
答:。。。
问:项目和框架。
答:(被鄙视不是大数据项目),自己谈到不熟悉框架,自己本科生相对于项目更注重基础。
问:Java 8函数式编程。
答:聊了聊Collection接口下的stream内部迭代和平常for循环外部迭代区别。
问:回调函数,函数式编程,面向对象之间区别。
答:不太清楚,就说了说面向接口编程的看法。
问:面向对象的三大特点,接口和抽象类区别?
答:。。。。
问:了解集合类吗?
答:因为在算法中使用的多,加之看过一点源码,说很熟悉。
问:ArrayList和LinkedList?
答:从底层数据结构的数组和链表实现入手,谈了插入,删除,查询时间复杂度。
问:HashMap。
答:谈了谈源码。其中的静态内部类Map.Entry的成员和Entry数组。通过hash算法定位数组下标,然后通过拉链法解决hash冲突。
问:HashMap的存放自定义类时,需要实现自定义类的什么方法?
答:hashCode和equals。通过hash(hashCode)然后模运算(其实是与的位操作)定位在Entry数组中的下标,然后遍历这之后的链表,通过equals比较有没有相同的key,如果有直接覆盖value,如果没有就重新创建一个Entry。
问:HashMap的负载因子?
答:0.75。
问:挺熟悉HashMap的,那你了解ConcurrentHashMap吗?
答:(碰巧前不久才看了几次源码,偷笑)扯了扯ConcurrentHashMap源码。其中有一个Segment数组,每个Segment中都有一个锁,因此Segment相当于一个多线程安全的HashMap,采用分段加锁。每个Segment中有一个Entry数组,Entry中成员value是volatile修饰,其他成员通过final修饰。get操作不用加锁,put和remove操作需要加锁,因为value通过volatile保证可见性。
问:volatile关键字。
答:(通过上一个题扯的,面试官就顺带问volatile关键字)volatile的原子性,可见性,禁止重排序以及底层通过插入内存屏障来实现的可见性和禁止重排序。
问:Java内存模型了解吗?
答:只谈了一点最低安全性保证。
问:有没有遇见过内存溢出的情况?
答:回答没遇到过,就从自己了解的内存泄露扯到ThreadLocalMap源码分析,什么弱引用,解决hash冲突的办法和HashMap不一样,使用的是开放地址线性探测法,而不是HashMap采用的拉链法,和为什么ThreadLocalMap可能引起内存泄露。
问:ThreadLocal为什么保证线程私有?
答:(面试官看我扯到ThreadLocal就往这方面问)Thread有一个成员是ThreadLocalMap,所以每次调用ThreadLocal的set和get方法都是从每个线程私有的ThreadLocalMap中进行插入获取元素。相当于每个线程都有自己的map,虽然线程都共享ThreadLocal这个键,但是都是从自己的map中进行获取元素,自然保证线程私有。
问:线程池了解吗?
答:说了一般线程池有corePoolSize,maximumPoolSize,任务队列,等待时间这几个比较重要参数。(1)当线程数小于corePoolSize时,直接创建线程执行task(2)当线程数大于等于corePoolSize时,将task放入队列(3)当队列放不了task时,又创建线程来执行task(4)当线程数大于maximumPoolSize时,根据不同策略抛弃task之类的。又谈了谈Java中Executor框架对线程池的实现。比如FixedThreadPool,SingleThreadExecutor和CachedThreadPool之间区别,和它们构造时,上述几个参数的不同。
问:JVM内存结构?
答:pc寄存器,方法区,Java方法栈,本地方法栈,堆,其中又谈了每个部分是否多线程安全。其中Java方法栈又扯了扯栈帧,栈帧又是由局部变量区(用数组下标实现定位局部变量),操作数栈(和局部变量区不同,是真的通过类似栈的push和pop操作,保留中间运算结果)和栈帧数据区(异常表,用于常量池解析和返回值)。
问:Java垃圾回收。
答:引用计数及其缺陷,copy算法,标记清除和整理碎片(然后在回收器方面答得不好)。
问:快排。
答:从快排的partition方法入手,大概分析了为什么平均时间复杂度为线性对数级别,又分析了为什么最坏情况为平方级别。
问:动态规划。
答:(因为自己写着熟悉动态规划)因此问了我怎么遇见动态规划和第一次使用动态规划(我说我第一次是在蓝桥杯中一道金陵十三钗中学习动态规划),然后我就扯了扯动态规划的特点(空间换时间)和定义(当前子问题的最优解会构成下一阶段问题的最优解),然后说了说迷宫中捡石头,从左下角到右下角要最多的石头,(x,y)坐标的最优解就可能由(x-1,y)和(x,y-1)的最优解构成。(反正东扯西扯啰嗦了答的不是很好)。
问:从10亿个数中找不重复的数(听成从11个数中找不重复的,心想怎么这么无聊的问题)
答:将10亿个数排序后存在不同子文件中,每个子文件在内存中用HashMap来进行判断,比如放入map中是(int和boolean它们封装类的键值对),第一次放进去时候boolean为false,当map中有这个数之后再放进去时,将false改为true。最后遍历map找出为false的数就是不重复的。
问:TCP为什么三次握手,而不是两次?
答:。。。。
问:TCP为什么是四次挥手,其中TIME_WAIT和CLOSE_WAIT这两个阶段。
答:TIME_WAIT阶段要等待2个MSL时间才关闭,因为网络原因可能要重发。CLOSE_WAIT是要等待自己(通常是服务器)把自己传输东西发送完了。
问:select和poll区别?
答:(不清楚)只说了说是IO复用模型。
问:项目架构。
答:。。。
问:Java 8中stream迭代的优势和区别?
答:stream迭代可以发挥多核优势,可以并行处理,而不像外部迭代需要串行处理。
问:框架封装jdbc受检异常的考虑和原因?
答:说了说代码更加简洁和有些sql异常就算catch到也无能为力,其他基本瞎说。
问:处理器指令优化有些什么考虑?
答:说了说synchronized同步成功和volatile的保证可见性的比较。保证了可见性不等于正确同步,因为还有原子性没考虑。
问:synchronized和Lock一些区别。
答:Lock多出的三大优势(尝试非阻塞加锁,尝试超时加锁,尝试可相应中断加锁)。
问:synchronized可重入吗?
答:是(其实可以举例证明,比如你自己递归调用假如不可重入不就死锁了吗)
问:Lock内部实现。
答:通过AbstractQueuedSynchronizer队列同步器实现。谈了谈队列同步器中的一些重要方法,以及一般自己怎么实现一个锁。
问:synchronized可以替代读写锁吗?
答:不能。本来想说Java中的读写锁也是通过队列同步器通过高16位和低16怎么实现的,但被叫停,不用扩展。。。。
问:当获取第一个获取锁之后,条件不满足需要释放锁应当怎么做?
答:自己说的通过while+wait的通知唤醒机制实现,但好像面试官想听到的是另外的方法。
问:知道线程的中断吗?
答:线程的Interrupt中断,其实只是修改线程的中断位,并不会真正停止线程。(1)可以通过Thread的类方法interrupted检查当前线程是否中断,但是会重置标志位(2)通过调用Thread的普通方法isInterrupted来检查中断位,不会重置标志位。
问:既然线程调用中断方法不会停止程序,那么有什么用?
答:谈了谈线程池中,线程销毁,假如没有中断那么就一直while检查不断执行任务。如果中断后,就return方法。就是return Runnable的run方法,线程运行完毕自然就可以销毁。(下来查资料看到真正的中断用法)其实中断线程有很多方法:(1)通过检查标志位用break退出循环(2)通过return退出run方法(3)通过对有些状态中断抛异常退出。
问:Collection下面的接口。
答:List,Set,Map(其实没有Map,要是多想想Map一直都是使用put方法,没有听说过使用Collection的add,就不会回答这么愚蠢)。
问:还了解除util其他包下的List吗?
答:想不出来了,经提示有CopyOnWriteArrayList,又谈谈它的使用场景,读操作远远大于写操作的时候,修改的时候会拷贝List。
问:CopyOnWriteArrayList多线程安全吗?
答:是。
问:反射能够使用私有的方法属性吗和底层原理?
答:不知道,就说了反射会破坏封装性,因此可以使用这些私有属性。
操作系统:
问:32位系统的最大寻址空间?
答:2的32次方吧(注意回答的时候可以更加自信一点)
计算机网络:
问:在不使用WebSocket情况下怎么实现服务器推送的一种方法。
答:(当时没听懂意思,以为要在HTTP1.1版本上做文章,就说不知道)其实下来查资料可以使用客户端定时刷新请求或者和TCP保持心跳连接实现。
Linux:
问:怎么查看进程占用资源?
答:top。
问:怎么查看网络状态?
答:netstat。
问:查看磁盘读写吞吐量?
答:不清楚,就说的free。
问:查看80端口连接数。
答:不清楚,就说的netstat然后加管道count。
Sql:
问:问了一个这样的表(三个字段:姓名,id,分数)要求查出平均分大于80的id然后分数降序排序。
答:有段时间没有写Sql了,想了半天,还想什么各种子查询,然后经过提示用聚合函数avg。select id from table group by id having avg(score) > 80 order by avg(score) desc。
阿里电话三面, 1 个小时
问:自我介绍。
答:还是按照简历上背。。
数据结构和算法:
问:介绍你了解数据结构。
答:前两面基本没有问这方面,终于等到自己擅长的了。最基本数组,链表不用多说。队列,栈可以用来分别实现广度和深度优先遍历。树,聊聊用于内存中搜索常用的二叉树,时间复杂度对数级别,但是容易退化成线性查找。因此聊到平衡二叉搜索树,又聊到红黑树(因为简历上有写着实现过红黑树),红黑树的几个雷打不动的性质。又谈了谈线段树,树状数组的应用场景。最后又说了说磁盘搜索的B树和B+树之间区别和应用。以及为什么磁盘搜索都是多路树而不是二叉树。
问:判断二叉树是否为平衡二叉树。
答:通过变形的求深度的算法。只不过当子树不平衡时返回-1,告知父节点子树为不平衡。最后判断返回值是否为-1。
问:10G文件的淘宝商品编号,只有512M内存,怎么判断究竟是不是合法编号(即编号是否存在)。
答:按照内存大小block,来计算10G/block,算出需要block个数,最后像HashMap一样通过hash计算编号的hashCode来求余定位子文件位置,最后再加载进内存中二分查找。
问:假如淘宝存着一个包含10w个敏感词的词库,紧接着需要从多个商品标题中随机抽查3个有没有包含敏感词的商品。
答:(因为之前谈数据结构时说了自己了解过Trie图,所以面试官说这个问的是加大难度的)可惜当时只知道用26个英文字母数组表示的Trie图,没有答上来。
计算机网络:
问:浏览器访问淘宝官网域名,浏览器的一系列步骤。
答:。。。
问:仔细谈谈DNS解析。
答:使用udp协议,然后递归查询和迭代查询之类的。
问:TCP 的三次握手和四次挥手具体过程。
答:。。。
问:UDP和TCP区别。
答:。。。
操作系统:
问:进程线程区别。
答:。。。
问:进程通信方式,有这方面的编程经验吗?
答:因为自己说学习Java,是线程的编程,进程只有在以前Linux网络编程接触过一点,因此谈到了Java线程方面。
Java
问:创建线程三种方式。
答:继承Thread,实现Runnable和线程池。
问:线程池用法。
答:。。。
问:Class加载了解吗?
答:装载->连接(验证,准备,解析)->初始化。其中必须按照顺序,只有解析可以延迟到使用的时候。
问:Java类信息方法信息存放在哪里?
答:方法区。
问:仔细谈谈JVM内存结构。
答:。。。
问:堆的各个分代。
答:。。。
问:了解分布式吗?
腾讯一面, 30 多分钟
因为自己是搞Java的,所以完全没有C/C++的问题,面试官人也很好,叫我不要紧张,就当作聊天,全程问的基本都是简历上的。你怎么证明自己熟悉数据结构?自己说实现过红黑树,然后谈了谈红黑树的性质,本来想举例所有的插入调整和删除调整,面试官说穷举情况太多就不用了。红黑树什么时候使用,以及它的好处?从二叉树说起,容易退化成线性查找,然后是平衡二叉搜索树,但是维护太麻烦,带来的收益远远不如红黑树,自己又强调红黑树用于内存搜索,磁盘搜索用的多是多路树。(嘿嘿)紧接着就开始问磁盘搜索,因为磁盘 读取效率很慢的,读取次数是基于树高度,所以像数据库中的B/B+索引都是多路树,每一个block中存放多个子节点信息,减少磁盘读取次数,然后又谈了谈B树和B+树区别。然后又问红黑树使用在哪些地方,自己说Java中TreeMap和TreeSet都使用了红黑树,感觉面试官好像不太满意,又说epoll中也使用了红黑树。接着又问线段树,树状数组的使用场景(因为自己简历有写着实现过这些)。又谈了谈项目中的一些优化。最后以一道算法题结束,将C中字符串中每个字符高4位和低4位转为16进制表示,最后返回转化后的字符串(当时没有读懂题,不知道是转为16进制,很别扭,而且Java中char可能是两个字节表示)。当面试官问我有什么问题的时候,自己以为要挂了,因为确实都是自己在说,感觉面试官没有问几个难的问题。所以当时表示自己其实基础很好,还有很多地方自己都有深入学习,然后本来想谈谈笔试中一道题自己怎么理解错了,思路是怎样的,后来面试官说时间不够了,然后知趣的离开了。
腾讯二面
没回答起一个问题,对的,你没有看错,确实没有回答起一个问题。
京东一面, 35 分钟(本来预约到4点的,当时3点过还在和腾讯hr姐姐聊天,等hr面试完了后发现,3:20多就打电话过来了)
问:自我介绍。
答:。。。
问:你说你熟悉JVM,那你说说你熟悉的具体哪些方面嘛?
答:当时就觉得问的好广,就挑了挑JVM中的内存结构,GC回收,还有类加载过程。
问:那你谈谈类加载过程。
答:装载,连接(验证,准备,解析),初始化。然后又具体谈了谈准备阶段,准备阶段主要是分配必要的内存,然后给这片内存初始化为默认的值(强调了这个初始化的默认值是固定的,比如boolean变量就是false,int变量就是0,和程序的代码中的初始化无关)。然后谈了谈解析过程,解析过程其实可以延迟到使用的时候,主要是把符号引用解析成本地指针,因为在常量池中有字面常量(比如Integer,Double这些常量是字面的值直接就可以用),但是符号引用(像Class,Filed,Method这些常量是由类的全限定名加一些描述符表示,在使用的时候需要具体解析到本地指针)。然后初始化阶段才是代码中的那些初始化语句进行变量初始化。
问:那你谈谈JVM中内存结构。
答:。。。
问:内存回收呢?
答:谈了谈内存回收是在堆上进行的,谈了谈堆的分代和常用的垃圾回收算法,然后又谈了谈具体的几种内存回收器。
问:那你怎么使用某种收集器呢?在哪个时候进行参数设置?
答:(翻白眼)当然是在Java运行的时候啊。(当时面试官不满意,追问我在具体哪个阶段)就说了具体的Java文件,字节码文件,Javac命令和Java命令使用,在Java命令运行Class文件的时候进行参数设置。
问:G1收集器是在哪个版本出来的?
答:不太清楚,就说是1.5之后。
问:那你知道内存GC的时候,要使用Object的什么方法吗?这个方法在什么时候使用?
答:finalize方法,在进行第一次标记的时候,如果发现GC Root不能触及到时,就调用这个方法,如果该对象还没有复活,那么在下一次就进行回收。这个方法不管是否复活,都只调用一次。
问:你谈谈Java8的一些新特性。
答:主要是Lumbda表达式和stream流迭代。
问:哪些容器有forEach方法?
答:记得Map用过forEach方法,就说好像只有Map有。(在面试官的旁敲侧击下成功蒙错答案,其实是Iterable和Map接口下都增加了默认方法forEach)
问:(接下来面试官就死怼框架和项目)你使用了哪些框架,你谈谈Spring框架?
答:(对框架的确不熟悉)就只从IOC和AOP粗浅谈了谈。
问:SpringMVC中来一个请求具体的过程。
答:首先有DispatcherServlet进行分发,按照HandlerMapping进行分发到具体的Controller上,然后调用Service服务层获取model,最后将model返回给前端渲染。
问:你项目中拦截器是怎么使用的,你Controller类有哪些注解?
答:重写了拦截器的preHandle方法,然后在配置类中进行重写WebConfigAdapter(具体记不太清楚了)进行注册。Controller中有@RestController,@RequestMapping的类注解,然后成员上有@Autowired注解,然后方法上还有@GetMapping或者@RequestMapping注解。
问:AOP是怎么***实现的?
答:Spring中AOP有JDK和CGLIB***,谈了谈两者区别。(不过感觉好像面试官想听到的是反射)
问:连接池用的是什么?
答:c3p0,当时还没从腾讯HR面试中的谈(吹)人(牛)生(逼),知无不答的状况中走出来,心血来潮的说用了SpringBoot后,用的Spring自带的数据源,没有再配置过连接池了(自己挖坑啊)
问:咦,那你说说Spring中自带的数据源?
答:(当时懵了,自己看过,但是一个不记得了)就说自己好像记得一个单线程的数据源(做死啊,其实没有这个)
问:那你使用的是这个单线程的?
答:(我怎么可能用单线程这么low的)果断回答没有(其实根本就没有这个数据源)自己想着不能让面试官再问了,就说自己在application配置文件中配置过数据源,在Java配置中也用Java代码配置过数据源,但是具体的记不得了。因为自己没有看过源码,框架这方面的确不太熟悉。
问:(幸好没有再问框架了)谈谈数据库中一二三范式吧。
答:。。。
问:那你谈谈对范式的理解?
答:范式越高,数据冗余越少,但是表链接时效率更低。范式低,数据冗余高,但是查询会更快,具体的时候还是看一个具体的空间和时间的折中吧。
问:我看你项目中使用过Trie图,你谈谈吧。
答:谈了谈具体的数据实现,然后建树也就是每次插入字符串的时间复杂度是线性级别,通过BFS进行多路KMP获取回溯失败指针。
问:那你这个数据结构放在内存中,要是服务器挂了或者进行数据修改咋办,考虑过共享内存吗?
答:自己的确没有接触过分布式,都是单机的,Trie修改是每天一次,如果在建树时进行搜索,都是在磁盘进行的。本来师兄给我的建议是使用ElasticSearch进行全文搜索(挖坑中),但是因为自己不熟悉,所以还是使用的Trie图。
问:咦,你还了解ES啊,谈谈呗。
答:(我着实不了解啊)就谈了谈反向索引和分词
一面完后,自己总结到千万不能挖坑,二面的时候一定一定要谈自己熟悉的,千万不要和面试官扯框架,多和他聊基础。
京东二面, 30 分钟(本来预约11点,结果过了快半个小时才打电话过来,幸好看了牛油的情况,提早啃了个面包)
问:自我介绍(打断了我几次,给我莫名的压力)
答:自己叫什么,熟悉什么什么,因为是在校生,没有接触过企业级项目,所以没啥实际的经验,框架也不是很熟悉(话音刚落)。。。
问:那我们来聊聊框架吧,你说你熟悉什么框架?
答:(奶奶的,我有说我熟悉框架啊)Spring吧。
问:Spring中bean是单例的还是多例的?
答:默认是单例的,可以申明注解为多例的。
问:那分别什么时候使用单例和多例?
答:(心头一震,没用过多例啊)就说像Controller这些类都是单例的。
问:面试官不满意,追问什么时候使用单例和多例呢?
答:平时都使用的是单例的,确实没有用过多例的,那么我觉得可能在A需求要改变某个初始值的时候,B需求想获得这个初始值的时候可能使用多例。
问:还是不满意,举个具体的例子?
答:(不好糊弄啊)就说如果有个组件记录的是每个人访问资源的次数,那么A用户访问了两次,接着B用户再来获得这个组件记录访问次数,这个时候单例肯定就不行了,因为A用户的次数会加到B用户上。
问:你使用的SSM是Struts框架吗?
答:不是,使用的是Spring,Mybatis,SpringMVC。
问:那你使用的什么连接池呢?
答:(肯定一面面试官反映了自己连接池这里不太清楚)c3p0。
问:那不使用连接池可以吗?
答:可以啊,只用数据源就行。
问:数据源和连接池区别呢?
答:自己就从线程和线程池这里来说的,连接池就像线程池一样管理着连接,c3p0可以配置很多参数,因为每次去new一个连接这是很慢的,不如直接就创建多个连接,然后每次进行分配任务就行了。
问:你使用过中间件,缓存这些吗?
答:没用过。
问:那你知道缓存吗?
答:缓存又叫快取,就像IO操作,把常用的数据提前放到能够进行更快获取的地方,这样每次就从内存进行查找,如果没有再去磁盘查找。
问:Sql写的多吗?谈谈内连接和外连接吧。
答:内连接就是两张表条件满足的情况下,才把这些行记录进行罗列出来。外连接就是两边类似乘积一样。
问:当时面试官就说不对,你这是迪卡尔积,我说的外连接是左连接和右连接。
答:(难道我把外连接记错了)左连接就是把左面的全部列出来,然后右面表的满足条件的就把字段写上去。右连接相反,把右面表的字段全部列出来,然后左面表满足条件的字段写上去。
问:那条件不满足的字段呢?
答:那就为空呗。
问:你还会其他什么技术?
答:(奶奶的,轮到我装逼了吧)我还熟悉常用数据结构和。。。(还没说完就被打断)
问:我们都是在场景中具体使用才谈(潜台词就是小兔崽子别给我整虚的)那我们来个这样的场景,突然增长了很多请求,原来的每个请求都会调用一个具体的业务方法,希望怎么能够减少调用业务方法的次数?
答:(心想不妙)就说肯定要使用缓存,比如A商品可能被重复访问多次,那么把这些信息可以放到内存里面。
问:缓存肯定是要使用的,但是你并没有减少调用业务方法的次数啊?
答:(真难糊弄,冥思苦想)突然想到这个需求不就和你前面问我的中间件有点像,可以先在中间件进行一个统计计数,然后去重后再来服务器调用。
问:(终于满意了)听说你还会ElasticSearch?
答:我只是了解过,不会啊。
问:网络编程三层,两层了解吗?
答:不知道,就说了说OSI的7层。
问:看来你网络这方面不了解。。。那你Linux会用吗?
答:会些简单命令吧。
问:比如查看一个进程具体信息,打开了哪些文件描述符,打开的端口,上一次占用CPU率怎么办?
答:不知道啊,就懂基本的netstat和ps命令。
问:看来Linux你也不了解,那你会前端的技术吗(具体什么技术记不得了,确实不知道)
答:不会啊。
问:那你项目怎么做的?
答:前后端分离的,我只负责后端的。
问:那你总接触过前端吧?
答:没接触过。。。
问:面试官控制不住自己了,没听清楚是笑了还是爆粗口了。
答:。。。。
问:后来又给我介绍了介绍他们的部门,强调了这是搞技术的,不是业务的,业务的话就是来一个需求就写一个方法,技术的就是什么安全监控,日志监控,流量控制之类听起来就很牛的。
欢迎大家指出错误 让我修改,希望该面经不会误导别人哈。