NJQ面经

终于收到了家正规公司的面试通知了,面试实习生,问的问题都是非常基础,非常底层的,好在之前也复习过一部分,但也有几个问题没有答上来,先总结总结面试经验吧。

面试我的是一个中年圆脸大叔,看上去像个搞技术的(没有传说中的秃头)。

面试官:你大学都学过什么课程。

我:大一的时候学了C语言,感觉学的不够深,而且越学越觉得C语言坑多,后来又有数据结构、计算机网络、操作系统,大三也就是上半年还学了个编译原理。

面试官:编译原理学的怎么样。

我:编译原理好难,但是学校考试还是蛮简单的。

面试官:你是网络工程专业的,那你都了解哪些网络协议。

我:我最熟悉的是HTTP协议,因为以前做过安卓,HTTP协议用的比较多。

面试官:那你说说HTTP协议头信息。

我:嗯。。。HTTP报文分两种,一种是Request请求报文,一种是Response响应报文。这两个报文头部都有一些实体信息,比如Content-Type、Content-Length;还有就是请求头中比较常见的是Cookie头字段,每次客户端发送请求的时候都会在这个头字段中加入网站的Cookie信息,另外响应头中还有个Set-Cookie头字段与前面的Cookie头字段对应,用于服务端向客户端设置Cookie。HTTP中还有很多的头信息。(刚说完想了想,忘记说爬虫用来欺骗服务端检测的User-Agent字段了)。

面试官:如果叫你封装HTTP协议,你会怎么封装。

我:首先要对输入流过来的字符串进行解析吧(思考怎么回答这个问题,后来联想自己看过的HttpClient的源码)。我对HTTP报文格式挺熟悉的,我来说说怎么把报文各个部分进行封装吧。首先请求报文中第一行是请求行,请求行分成三个部分,请求方法Method、请求的URI、还有一个字段…有点忘了(窝草,协议版本都忘了)。可以把这三个部分作为字段封装在HttpRequest中,方便获取。然后请求头是一个个的Key-Value对,可以将每一个请求头封装成HttpHeader对象,然后多个请求头组成一个List集合。空行之后,就是请求体了,这个可以用HttpEntity对象进行封装,当然请求体类型非常多,可以是HTML,CSS,JS这样的文本内容,也可以是图片这样的二进制内容。(说着说着把响应报文给忘了,不过报文格式差不多,影响不大)。

面试官:你觉得这样效率高吗

我:肯定不高,但是便于操作和管理。我以前看过HttpClient的部分源码,它也是这么封装的,还有以前做安卓,用过OkHttp,差不多也是这样子。

面试官:这两个库我以前都封装过(给我一种装逼的感觉,我无语,不知道是自己造一个HttpClient呢,还是对HttpClient进行二次封装),嗯,那你觉得这两个库有什么区别。

我:HttpClient体积有点大,比较适合服务端使用,OkHttp轻量,适合安卓客户端。(看面试官没有接话,好像还不满意,我有画蛇添足的加了句),HttpClient更容易扩展,OkHttp可能扩展性不怎么好(说完我就后悔了)。

面试官:HttpClient和OkHttp扩展性都很好。…你觉得HTTP协议有什么缺点。

我:客户端发送一次请求,服务端发送一次相应,Http连接就断开了。服务端无法向客户端推送信息。比如安卓app里面很多的应用都需要消息推送。(后来想想这个描述的不简洁,应该这样打,HTTP最主要的两个缺点是不支持长连接以及不安全,两者的解决方案分别是WebSocket和Https)

面试官:你有什么解决方法。

我:最简单的方法就是客户端轮询,让客户端定期向服务端发送请求。还有一种就是使用WebSocket技术,但我还没有深入研究过WebSocket,所以没办法给出完整的解决方法。(时候立马上网学习了一波WebSocket,并不是什么很难的东西)

面试官:嗯,WebSocket可以实现,客户端轮询的方法对于安卓来说比较耗电。你还有没有其他的解决方法。

我:(本来想回答个Http长连接的,但自己对这部分内容不是太熟悉,怕弄巧成拙)暂时还没有。

面试官:嗯,那你对TCP协议有多少了解。(终于换话题了)

我:三次握手,四次挥手,窗口滑动。

面试官:那你先说说三次握手和四次挥手。

我:(暗喜,还好前一个月在学校就稍微复习过)客户端发送请求报文,里面有两个标志位:SYN和ACK,SYN就是Synchronize同步的意思,表示客户端要求开启连接,还有ACK,Acknowledge确认,(这个单词怎么念,都想了好几秒),用于握手过程中标识确认报文。客户端先随机生成一个sequence序列号,同时将SYN标志位置为1,然后把报文发给服务端;服务端接收到报文后,也随机生成一个sequence序列号,并将ackownledge确认序号设为客户端序列号加1,并将SYN和ACK置为1,然后发给客户端确认;客户端接收到确认报文后,ackownledge确认序号设为服务端序列号加1,同时将ACK置为一发给服务端确认。(竟然忘了说SYN泛洪,虽然老师上课只提到了一下,我下课还专门查过这个概念)。 然后,四次挥手中要用到一个FIN标志位,也就是Finally终止的意思。关闭连接可以是服务端或客户端任意一方发出,比如客户端请求断开,往服务端发用一个FIN请求,然后FIN加一(后来才发现不对,标志位怎么加一呢,应该是将确认号设置为收到的序列号加1),然后发给客户端确认,服务端收到后立马发送一个确认报文;然后服务端再发送一个FIN给客户端,客户端以同样的方式确认。需要四次握手是因为TCP是全双工的,两个方向都需要等数据发送完后再关闭。而握手只需要三次,是因为中间有一个报文同时起到了确认和同步的作用。

面试官:再说说窗口滑动机制。

我:(窗口滑动,忘得差不多,而且前面说完头都快被自己绕晕了,就不装这个逼了)这个时间有点久远忘的差不多了,因为是大二上学期学的(感觉是自己挖坑往里面跳)

面试官:学网络专业的,你这方面还不够扎实啊。(果然如我所料,网络专业网络知识问的比较多,好在也稍微复习了一下,不然“整段垮掉”)

面试官:学过数据库没。

我:大二的时候学校教过SQLServer,但是是傻瓜式的图形界面。自己也玩过MySQL的命令行操作。

面试官:不管学什么数据库,原理都一样。

我:嗯,SQL语法上可能会有细微的差别。

面试官:数据库方面,你都会哪些内容。

我:查询语句,还有 增删改操作,创建表,创建数据库,创建索引等等(说后面两个的时候有点心虚,因为用的并不多)。

面试官:你知道索引有哪几种吗。

我:(完了,这个GG了,果然自作孽不可活)两种吧。

面试官:你确定只有两种。

我:我了解的SQLServer中好像有两种。

面试官:用什么区分

我:通过在语句中加unique进行区分。(心里是崩溃的)。

面试官:(好像不满意)表的连接有几种。

我:(搜寻印象中…)left join, right join,full join。

面试官:有什么区别。

我:left join影响的是前一张表,right join影响的是后一张表,full join影响的是两张表。

面试官:什么是影响,能说的更详细一点吗。

我:left join选取的是前一张表的字段,如果后一张表中没有这项数据,就会用NULL填充。right join选取的就是后一张表的字段。full join选取两张表中都有的字段。

面试官:那你了解Hibernate,MyBatis这样的框架吗。

我:了解过Hibernate,他们都是ORM框架,把数据库中的表映射成Java中的对象。而且Hibernate中还有自己的查询语言HQL,是面向对象的查询语言,把表中的字段映射成对象的字段。

面试官:MyBatis呢。

我:MyBatis还没有研究过。不过这些ORM框架应该基本都遵循JPA规范,新项目中如果面向JPA编程,就可以使用任何符合JPA规范的ORM框架,换ORM框架比较方便。

面试官:使用JPA,如果出现Bug,怎么找,你有没有想过。

我:(框架还没怎么学啊ε(┬┬﹏┬┬)3)目前做的都是些小Demo,所以还没碰到过。

面试官:那你会Spring吗。

我:Spring是个很大的框架,我不敢说会Spring。

面试官:你使用过Spring的什么功能。

我:IOC

面试官:你说说IOC的原理

我:通常我们创建对象都是使用new关键字,更好一点会使用工厂模式来负责创建对象进行解耦,而IOC就不需要你自己去创建对象,直接到IOC容器中去取就可以了,Spring中IOC容器就是ApplicationContext。

面试官:其实IOC也是使用工厂模式。

我:嗯,就是BeanFactory接口,ApplicationContext封装了BeanFactory(感觉框架部分整段垮掉,毕竟没有做过项目,而且也没怎么复习)

面试官:并发学的怎么样。

我:还好,能理解里面的原理,看过里面的源代码。

面试官:那你知道barrier吗

我:啊。。。(这英语和我有的一拼,说的我一脸懵逼)

面试官:就是栅栏。

我:有一个CyclicBarrier类(Cyclic不会念,我就说是循环),与之类似地还有CountDownLatch,和Semaphere。

面试官:Semaphere是干嘛用的。

我:操作系统时候也学过Semaphere,信号量,这和Java中的信号量功能差不多,比如说你有10个资源,你可以在Semaphere构造函数中传入10,创建10个信号量用来代表着10个资源,要使用就申请,当资源数为0是,申请就会发生阻塞直到资源数不为0。

面试官:并发库中你还了解什么。

我:线程池。因为操作系统创建线程销毁线程都比较耗时,线程任务执行完了就把线程销毁了,有点浪费,于是就有了线程池,线程池本质上就是使用了享元模式。在并发库中有三个接口可以表示线程池,Executor,ExecutorService,ScheduleExecutorService。这三个接口层层递进,其中Executor没有直接实现类,ExecutorService中有一个实现类(脑子短路,突然忘了那个类的名字,ThreadPoolExecutor),这个类的名字有点忘了,但是有一个Executors工具类可以创建线程池对象,常见的有固定大小的线程池,可扩容的线程池还有单独线程的线程池。

面试官:数据结构算法学的怎么样。

我:大一的时候学的,考的还不错。(以为要问数据结构,自信心又来了)

面试官:背包问题知道吗。

我:啊(一脸懵逼,不按套路来啊),闭包。

面试官:背包问题。

我:背包问题,没了解过。

面试官:知道二叉搜索树吗。

我:知道,二叉搜索树也叫二叉平衡树,最常见的就是红黑树,Java集合里的TreeMap就是用红黑树实现的。

面试官:能说说里面的原理吗

我:二叉搜索树就是父节点左边的节点小于父节点,右边的节点大于父节点,插入时根据大小比较插入到树中,通常要进行旋转来保持二叉树的平衡(真是哪壶不开提哪壶)。

面试官:知道怎么旋转吗,能详细的说说嘛。

我:有左旋转和右旋转。但是这个没有值,很难理清楚。

面试官:嗯,差不多了,基础还不够扎实啊。…. 你有什么需要问我的嘛。

我:公司里有多少做技术的。

面试官:70来个。

我:做Java有多少。

面试官:基本上都是Java的,1/3以内是前端,还有一小部分是做app的。

我:额,公司里工作时间是怎么安排的,加班情况频繁吗。

面试官:早上九点半,晚上五点半,中午十一点半到两点半吃饭休息。不过做技术的这个时间偶尔也有些变动

……

面试过程感觉不错,不像上次老板直接面试的,就看到老板在装逼,完全没有交流技术的机会。这次还好,聊了40多分钟,面试官很牛逼。当然面试过不过就听天由命了。。。

不会的知识点还需要补漏:

  • TCP状态转换过程

  • 滑动窗口机制

  • 背包问题

    还有就是不能被面试官牵着鼻子走。