百度2016Android实习生电话面试,从一面到三面

阅读向导:并不一定每个问题都有写到,整个面试3个半小时,有些不记得了; 文中写到的我的APP指的是我个人开发的南昌大学新闻客户端(NcuEveryDay)Github

面前确认

约在下午3点电话面试,按照短信提示准备好了纸和笔,等待面试。

这个时候其实有点慌,就在手机上找了首舒缓的音乐听着,反正也方便之后带耳机,缓解一下。2点50的时候,一个北京的电话来了,心想面试开始了,响了两声才接。我主动报上了姓名:“您好,我是xx”,“你好,这边是百度面试,请问您要面试的是iOS还是Android?”,“哦,您好!是Android”。“好的,稍后我们会有Android面试官对您进行面试,您方便接电话吗?”。“可以,没问题”。“好的,那就这样”。

这个时候才明白是面前确认,百度人事还是挺友好的,一直用“您”,于是我也是用“您”称呼,接着等着一面。

一面

3点,面试官准时打来电话。

我也先自报了姓名,简单认识后开始了正式面试。

面试官没有让我做介绍,后来我知道他手上是拿着我的简历的,也许是这个原因,也知道我做过的项目。他告诉我如果之后提问有不知道的就直接告诉他跳过,于是,技术问题就开始了。

1.你的App中使用了Activity+Fragment布局,你能说一下Fragment的周期吗?(因为我的项目介绍里说了这个)

因为才看过Fragment生命周期,所以这个问题倒是不慌,我说Fragment的生命周期要结合Activity来看,在Activity的Created阶段,要执行onAttach,onCreate,onCreateView,onActivityCreated;Activity的Start阶段会调用onStart,resumed阶段调用onResume…,最后的Activity的Destroyed阶段会与create阶段一样对应调用四个周期,其实开始上来有点紧张,说得好啰嗦,还有些抖~,后面就好了,还好,这个问题能够完整的答出来。

2.抽象与接口的区别,抽象类能不能创建实例,谁能定义变量

我说出了抽象类无法直接定义实例,必须要使用new关键字来创建子实例,比如说List list = new ArrayList,抽象类的方法要在子类里面实现,接口的主要作用是定义一些操作,只要实现了这个接口的类都能调用这个接口中的方法。其实一面对抽象和接口说的不是很清楚,面试官问还有呢,当时没有想出。他就问抽象类问什么不能定义实例,我说因为他没有实现具体的构造函数,这个时候不太确定了,就说要通过它的子类来定义实例,他就又问我难道抽象类的子类就不是抽象类吗,我说不是,子类如果没有实现所有的方法就还是抽象类,面试官说可以,一面结束后我就马上去查抽象与接口的区别,以免在后面被问到,果然,二面遇到了!

3.override与overload的区别

当时脑袋抽筋,只想到了经常用的@Override,没有用过@Overload啊,说我一般只用到override,不清楚overload,后来才知道原来面试官问的是重写和重载,考英语,还好后面直接问了重写和重载,还是能说出来。

4.你的APP中使用了多线程吗?

有。在进行耗时操作时需要使用子线程去处理。

创建线程的方式有那两种?

我平时使用了new Thread的方式创建线程,这个里面要实现run方法,还有一种是写一个子类继承自Thread类(其实也不太确定是不是这两种,官没有说什么,就说嗯)

那你为什么要使用子线程?

这个应该是问多线程的作用,我说在高版本的Android中UI线程不能进行网络请求等耗时操作,在低版本上可以,但是这样可能会造成界面卡顿,(这个时候我就说了一些更深入些的),AndroidUI界面每16ms需要刷新一帧,如果处理没有在16毫秒内完成,就会出现掉帧的情况,对用户来说就会出现卡顿,长时间没有反应就会出现ANR错误,所以将耗时操作放在子线程来做是需要的,即使在低版本上。官对这个表示可以,因为之前研究过Android优化,对原理了解的多,能够说出来。

子线程是怎么去更新UI的?

可以通过handler传递message给looper中的messagequque,looper遍历拿取message进行处理。(这个比较熟,能这样精炼答道,后面官也就没继续问了)

5.那你App中有没有使用过多进程?

当时就想每个App作为一个进程在os上运行,就说没有,一个App就只有一个进程,但是可以有多个线程,多个线程来处理。 那你用过service吗? 我立马发现自己说错了,不对,一个App可以有多个进程,service就是,我说sorry,这个刚刚打错了,其实判断一个App有多上个进程,其实只要在Android系统自带的应用管理列表中看,如果有多个进程,就会有多个App图标,否则就是一个进程(这个是经验之谈,当时也没有考虑是否可以通过其它方式修改这个列表) 那service中的数据怎么发到activity中? 我当时想到的是service与activity的通信,就说通过Intent,setAction发送不同的action给service,让service执行不同操作,因为之前做过音乐播放器的Demo,中间用到了service,broadcast service如何给activity发数据呢? 可以用广播,广播会通知activity来进行操作 还有呢? 如果用bind的方式创建service的话,service可以传回一个引用,通过这个引用来操作 如何操作的? 这个不太清楚 你有没有了解过Intent为什么能够在四大组件传递信息,它是如何做到的? 不了解,没有去研究过,我只能知道内部使用到了一个关键东西,是IPC(这个在Android开发艺术探索有写道,但是自己没研究) IPC~,这个是linux里面的东西啊,不过也算对!

6.了解集合吗?

嗯~,用过一些,比如ArrayList,用得很常用 collection用过吧? 只是知道,在我的项目中间没有使用,有的时候使用sort排序会重写一个compare方法 还有呢? 不太清楚 它有哪些方法? 其实不太清楚(因为当时没有想到List这些都是它的子类,就感觉自己没有用过,干脆说不太清楚,不给自己挖坑) 这个时候官停顿了一下,可能是在想下一个问题,突然很安静,我怕这样尴尬,就抓住机会,说说我对集合的其他了解,我说collection也是抽象类,无法直接定义实例,就像我们定义的时候要使用arraylist,hashmap一样,这个时候面试官就又有了提问思路 hashmap是collection的子类吗? 我说不是,应该是map的子类(其实是,collection下面分List和map) 你刚刚说到了arraylist,你知道arraylist与linkedlist的区别吗? 这个问题其实我是知道常考的,可惜后面忘了看,就只能直接说,不清楚,sorry,(如果不太清楚最好就直说,如果连续不知道就表示sorry,这样能显得更礼貌一些)。这时候面试官还说,没事,每个人知道的部分可能有些差异,听到这个,真的很欣慰,连说谢谢谢谢。其实感觉百度面试官都挺好的。

7.使用过jni吗?

学习过,是用java与c++混合编程,之前学习过一段时间,能写一些简单的方法,自己也会记录这些学习内容,因为我喜欢看大牛们的博客,所以自己也会搭博客,把一些学到的东西发在上面(其实我的简历上有些blog地址,我不知道他有没有去看),没有深入问jni

8.线程中的sleep方法与wait有什么区别

其实我开始没听清,还以为说的是sleep和wake,就先说了sleep,sleep是指线程停止,传入毫秒数 那wait呢? 这时候才反应过来,说的是wait,线程等待,脑袋里去想,其实没有很清楚,但是根据经验,我试探性的回答了,sleep不会让线程真正停止,如果有一个共享变量,这个时候其他的线程是无法使用的,但是如果是wait,其他线程可以使用这个共享变量,我用这个例子来说了我理解的区别,面试官问还有呢,我说不太清楚

9.在使用文件,数据库操作要注意什么问题

我开始觉得这个问题有点大,不知道要从什么来说,迟疑了一秒,就说像这些操作都是耗时操作,要放在子线程去做,这个之前说过,再就是使用完要及时关闭,因为像文件数据库都比较占内存,所以要及时关闭连接,释放内存(这个在研究内存优化的时候挺熟,也就说了,其实我之前特意准备了内存优化的问题,一是可能会问到,再就也可以主动说这方面,这样把自己了解深入的方面说出来,还是可以加分的) 还有呢? 一时也想不出,就说我一般使用的时候就是这样

那如果现在有这样的情况,有很多线程都去读取数据库,并发要如何保证数据安全性,比如一个线程在写的时候,其它线程也在写? 其实在我的App中也使用了数据库,我是使用了单例模式,这样保证全局只用一个数据库连接对象,避免了同时有多个数据库连接对象。

你想过使用锁吗? 了解这种方式,使用的是java的关键字synchronized,对关键代码部分加锁,这样其它线程必须等待线程执行完后才能执行这段代码。

10.你提到了单例模式,那单例模式如何实现,需要些什么步骤

单例模式首先要将构造函数设为私有方法,使外部不能通过new关键字创建实例,既然构造函数定为私有方法,那外部如何访问呢,所以下一步要定义一个静态方法,这个静态方法能返回类的实例,在其他类中,使用这个静态的get方法获取到。

除了单例模式,你还知道或者用到哪些设计模式?

比如观察者模式,这个主要是在rxandroid中使用到,rx就是基于观察者模式,实现了异步编程,一个操作开始后,可以继续做后面的事,当耗时操作执行完后,会通知订阅者执行对应操作,这个操作一般是UI操作,rx能够方便的进行线程异步操作。(这些基于我对于rx的了解,用过,能说)。

11.了解算法吗?

还算了解(因为我是又准备的,重新看了一遍数据结构,我看java版的,java版比起c版代码要更简洁一些,为了达到深刻的目的,必须达到手写算法,细看递归,根据我的面试经验,之前也有过面试,如果在回答的时候说会,那就必须能够在不查阅如何资料的情况下当场写出来,我觉得应该能直接手写出来的算法:二分查找,冒泡排序,选择,插入,栈,队列,优先队列,划分,归并,递归类算法比如:三角数与阶乘,全排列与组合,汉诺塔等这些经典的算法,熟悉快速排序,链表翻转,并且理解算法的时间复杂度,了解各种排序的优缺点。如果之前是学过c的数据结构的,现在再重看一遍并达到以上目的并不难,我是每天看一章,并对章内的算法反复推导理解透彻,大公司对于数据结构和算法的要求很高,所以我是刻意准备的,也下去做了很多经典的算法题,要求还是一样,透彻理解,要能自己复现,其实还是有些担心,怕一时想不出这个算法来,所以说了还算了解)。

那下面做一个算法题吧 好的

爬楼梯,一个楼层共有N步,可以一次走一步或两步,有多少种走法? 值得庆幸的是,这是一道经典的算法题,我是亲自手写过的,其实并不复杂,只是要使用递归,这是难点,既然知道,那就松了一口气,前面的算法题没有白做 我诚实的告诉面试官我做过这种题,然后迅速组织语言说了我的思路,说完他让我说一下递推公式,我回答正确f(n)=f(n-2)+f(n-1), 官说没错,他也许也看出了我在算法的熟悉度,有了写好的印象,所以说,多做题还是有好处了,只要面试遇到了一道,就算值。

冒泡排序与快速排序有什么区别? 问到排序我是很熟的,我说首先时间复杂度是不一样的,冒泡是n方,快排是N*logN,然后两者的优缺点,冒泡算法简单,效率低,适合数据量不是很大的情况,快排是一种算法复杂,效率高的算法,当有大量数据需要处理时,快排比冒泡要快几个数量级。我们的App作为客户端一般情况先,没有必要去实现快排。

12.逻辑题

其实我开始还以为又是算法题,又问了一遍题目的意思(如果面试时实在是没有清楚题目的意思,应该要问清楚,不然盲目做肯定会很混乱,效果不好),后面他说这不是编程题,其实就是一个逻辑题,题目是这样的:

一个商店有下列优惠活动,充值100送20元,会员九折优惠,求如果一个顾客在这家店长期消费,他的优惠额度有多大(没有太明白优惠额度是什么) 这个顾客是能同时享受两种优惠吗?(我问) 对 我的回答:我就以这100块钱来分析,他可以获得120块钱的消费金额,如果他这120块全部通过9折的方式用出去,就可以买120/0.9=133块钱的东西,但是他最开始只拿了100元,所以他能额外多消费33块钱,所以我的结果是33。面试官对于我的推导过程表示同意,但是他说结果为什么会是33,我问,他说是5/6,我其实也不太明白,于是就过了。我觉得他应该还是认为可以的。

13.平时如何管理代码版本

使用Git,这个应该是程序员标配了,我的简历里面写道了熟悉git,所以也不是很担心被问。

有上传过Github吗

有,我的这个App就是上传到github上的(这时候他应该是已经进去看了)

评价:
我刚刚进去看了一下,你的代码写得还可以,还不错 谢谢,谢谢您

你的情况还可以,基础稍微有点差,我会和他们交流一下,你可能还有第二次面试,但是也有可能没有,我要反映下才能知道结果。 好的,谢谢

你还要什么问题要问我吗? (一般都会问的问题)于是我问如果还有二面的话,大概是什么时间 那就是一会儿吧!还有问题吗 好,没有了,谢谢 不客气,那就到这儿了,你继续加油! (激动而忐忑,一面结束了,从3点到4点10,整整70分钟,自我感觉不算太糟糕…)

预感,还有二面,于是赶紧利用这个时间去查资料了,包括抽象与继承,ArrayList与LinkedList,collection…如果又被问到怎么办,事实证明,这完全正确,二面中又问到了抽象与继承的区别,于是近乎全面的回答。 一面除了刚开始有些紧张,说话都有些颤抖,后面还好。

二面

4点45的时候,又看到了北京的电话打来,看来二面来了。

这一次面试官让我做了介绍,认识了一下,他也许是听到一面说我java基础差点,上来就来了个大头的。

1.你了解垃圾回收吗?

了解

你能说一下垃圾回收机制吗,怎么实现?

一般采用引用计数,每一个对象有一个引用计数,当不再使用时计数减一,当计数为零时,就会被回收(还好,书没白看,博客没白看)

那垃圾回收是需要程序员来写代码还是怎么样?

不用,这是java与c++的一个区别,c++需要程序员自己写回收,java中系统自己会回收,调用的是System.gc方法。

为什么要有回收呢?

每一个对象都会占有一定的内存空间,如果不回收,最后会占满所有的内存导致App崩溃。

2.那你知道内存泄漏吗?

了解。内存泄漏指某些对象一直被引用或相互引用导致永远无法被回收,这种现象就是内存泄漏。这样也会积累消耗内存,导致App内存占用过多程序崩溃。

内存优化使用一些什么工具?

(优化专门准备过)我主要是使用Android Studio自带的memary monitor和sdk自带的heap viewer。

3.如果一个物理设备的内存为2G,那么你的App最多可以申请多少内存?

这个,不清楚

4.一个java类,如果不显式继承某个类,它有父类吗?

有,java中所有的类都继承自object类

那如果不写构造方法,能用new来创建实例吗?

能,有默认的构造方法

如果自己写了一个带一个参数的构造方法,还能像上面一样定义吗? 不能 (对于这种基础题,一定要对答如流,不要有可能,好像,这种猜测性词汇)

5.线程池了解吗

了解一些,线程池中有多个线程,这几个线程不会被回收,当需要新的子线程来处理耗时操作时,就直接从线程池中拿一个线程去执行,避免频繁的创建线程和回收线程,因为创建线程和销毁线程是很耗内存的。比如在AsyncTask中就有一个线程池,包含五个线程 那AsyncTask中多与五个线程怎么办呢,是等待,还是抢断,或者开辟新的线程? 开辟新的线程,AsyncTask中最多支持128个线程(AsyncTask还是挺熟的) 那在java中如何实现一个线程池你知道吗? 不是很清楚,但是我看过一些文章,关键的一个类是ThreadPoolExecutor,通过这个类,可以~管理线程(记不得) 如何管理? 不清楚

你能介绍下AsyncTask吗?

可以。(我说了常用的三个函数,每个的作用,参数传递,以及更新进度条等,没有问题)

6.你在做app时使用到了那些网络协议

(这个时候开始问非Android问题了,回答的不是很好,但也还是不至于缄口) http肯定是要用到的,其他的我好像~没有用到 那http协议包含哪几部分? 包含头部和主体 头部有哪些内容? 这时我开始犹豫不知道怎么答,不确定问的是一般我们在http请求中设置的那些headers下的属性还是计算机网络中学到的http头部内容,考虑到问网络协议,就答了计算机网络中讲到的,其实也不能记得很清楚。包括源地址ip,目标地址ip,头部长度,状态码(也不知道对不对),面试官没有说什么 那常见的状态码有哪些? 200,不是ok;404,无法访问;502,重定向,6以上的服务器错误 这个记得不对啊,面试官说:没有6以上的,5以上是服务器错误 哦哦,对对,记错了,sorry,想起来了,重定向是302,这块记得不对,sorry 没事,没关系

7.你了解数据加密吗?

嗯~~,不太了解,(这时主要觉得如果问加密算法肯定不会,倒是以前写过基于哈夫曼树的压缩,可以通过压缩达到加密的目的,但是哈夫曼树算法不熟,还是别说比较好,但是我又突然想到非对称加密,于是准备把我知道的这一小点说说也好)。不过我知道加密的两种方式,对称加密和非对称加密,对称加密可以通过加密后的数据通过一定的算法算出之前未加密的数据,但是非对称加密无法做到(其实也不是很确定) 那非对称加密有几种密钥? 两种,私钥和公钥 如果有私钥也不能反向获得未加密数据吗? 额~,不是很清楚,不记得了(这个时候也不知道怎么办了,面试最大的禁忌就是自己并不清楚还和面试官争论,甚至观点本身就是错误的,这样面试官会觉得你不懂装懂,其实诚实说就好了,不知道就说不知道)

8.文件上传用过吗

在我的App中因为没有涉及到,所以没有使用,但是如果要我来做的话,我会选择使用第三方库,比如Glide、Picasso等

9.fragment的生命周期(一面答过,无卡顿)

10.你知道View它是如何进行绘制的吗?

知道,这个研究过源码,主要是三个过程measure、layout、draw 在自定义View的时候,要注意些什么? 要自己去重写onMeasure、onLayout、onDraw方法。(我有研究过源码,关于measure流程,建议去看我的另一篇文章Android View总结

10.下面有这样一个场景,listview中的item中有一个button,要求点击这个button执行一个操作,比如说点赞,但是点击这个item其他的地方,跳转到其他页面,或者是打开一个dialog什么的,你要如何做?

(我很清楚,这题考的是事件拦截,所以不慌),这个要实现要处理的一个问题就是item会在button上面,导致点击button的时候,item直接相应而不会执行button的点击事件,那么我要重写item的onTouchEvent方法,onTouch方法有一个boolean类型的返回值,表示是否拦截触摸事件,如果返回false,就不拦截,这样这个触摸事件就会传递到子View上面去,返回true,就代表拦截,子view接收到这个事件,所以这个只要重写item的onTouch事件即可(其实这些平时也会用到,我讲了我之前自定义组合控件的时候用到的一个事件拦截的例子,面试官觉得答得还行)

11.用过广播吧

用过,之前在写音乐播放器的时候就是用广播来通知activity更新歌曲进度的。 知道静态广播和动态广播的区别吗? 嗯~~,动态广播在activity或service中创建,一旦activity或service销毁,广播就不存在了,静态广播能够保持存在(其实不是特别清楚) 使用service有什么好处? 首先它是一种多进程之间通信的方式吧,同时它可以通知多个进程,应用程序,多个app都能接收到这个广播,同时也可以通过定义filter来过滤广播消息(这题答得底气不是很足,用的少了)

12.数据存储有哪些方式

sharedpreference、文件、数据库 sharedpreference和db的使用场景分别是什么? sharedpreference主要是用到配置上,对于一些小型的配置,设置上用到,db用于数据量非常大的情况,同时db的强大之处在于它的查询、检索等特性上,使用sql语句可以方便的进行数据的查询。(这个时候画蛇添足,给自己挖了个坑),一般如果数据量小的话就用,sharedpreference,这样速度比较快,没必要使用db,db会更慢,而且占用更大的内存(挖坑完毕) 你知道sharedpreference内部是如何实现的吗? 不太清楚,但是我大概能猜到,应该是使用xml。(这个时候聊得还顺畅,根据经验,cocos2dx中UserDefault就是xml实现的,理性猜测xml) 对!确实是xml,(x读的叉,我读的x,以后记住了,免得以后显得不纯),你猜对了。sharedprefrence是使用xml文件来实现的。(面试官说) 哦哦。 所以你看,sharedpreference和db都是文件保存,问什么你觉得sharedpreference比db更快呢? (这个问题我懵了!只能找别的先挡一下刀)我说我是觉得db操作本身的sql语句执行,查询计算需要耗时(只能只样说了) 这个理由不够充分啊,其实呢,sharedpreference一些情况下是要比db快,那是因为它有缓存!都是文件操作,在有些时候,甚至比db还要慢。 哦哦,知道了,这个之前是错误的观点,现在懂了,谢谢您了,谢谢 不客气 (感觉这个面试官真的很好,他会在你不懂时,给你讲解,纠错,这次面试,收获颇多)

13.子线程更新UI有那几种方式啊?

我一般是使用handler,handler发送message到messagequque中,looper从quque中间获取到message,handler中的handleMessage就可以来进行UI更新操作了。(这些大家都耳熟能详了) 你的这个handler是属于哪个线程的? 属于UI线程 那他如何更新主线程? 在创建子线程的时候,可以在构造函数中间带参数,传递过去。(我以为我了解的可以,原来还有更深一些的东西我是没有了解到的,后面不会了) 还有呢? 额~,我又想到了以前读rxAndroid,人家对比的时候用了一个runOnUiThread的方法,但是这个我还真没用过,所以就说好像还有一个runOnUiThread的方法可以,但是我没有实际用过,用的最多的就是刚刚说道的handler ok,那还有呢? (真的想不出来了,只好发挥想象力说用广播也可以实现,子线程直接发广播,UI线程去接收更新数据,我感觉已经穷驴尽技了,广播都能拿出来了) 那如果现在有这样一种情况,这个子线程需要更新UI,但是它本身又是子线程创建的,如何更新UI? (也就是说它是无法无法通过这种传参的方式获得主线程handler,我表示真的不知道了)我说我只能想到广播了 面试官终于还是开始提示了我(再次表示感谢) handler有哪些创建方式? 我说可以用new Handler类创建,其它的方式不知道。 你知道looper吗? 知道 looper有些什么方法? looper最常用的方法应该是prepare和looper吧,如果在子线程要接受handler发送的message,遍历messagequque,需要手动调用looper.prepare,looper.loop,而主线程不需要,(这些我还是很清楚的),其他的方法我不清楚。 looper其实还有一个方法叫做getMainLooper,它可以获取到主线程的looper,而且可以通过looper来创建handler。 哦哦,原来还有getMainLooper方法,这个还真是不知道。 那现在你知道要怎么实现上面的需求了吗? 知道了,可以先通过getMainLooper来获得主线程的looper,然后往这个looper的messagequque中发消息就可以了。 要怎么发? 按照您刚刚说的,looper可以创建handler,获得了主线程的looper,再用这个looper创建一个handler,这个handler应该就是属于主线程的了,那用这个handler来发消息主线程就能够收到了,就可以更新UI了(还好,边说边想,当时反应还算快,一下想到了) 不错,就是用这种方式。 嗯嗯,谢谢了,这个这回学到了,之前不知道 通过传值过去传递handler,那如果说现在有这种

14.开放性问题(项目):让你来做一个app,图片浏览器,将设备所有的图片显示到列表中,显示微略图,要求图片加载流程,列表滑动流畅,性能好,你将如何实现?

关于如何去做这个项目,聊得非常多,对话很频繁,这里就不再啰嗦了。 期间问到的问题:如何获取图片(遍历SD卡),获取后怎么存储(放路径就行了),路径如何转化为图片(Bitmap.decode),如何选择性预加载(算法,内存等等),具体实现,如何减小图片占用的内存(Bitmap.config),计算100x100像素图片占用内存,list优化,图片加载库…聊得非常多,不过还挺愉快的。这个聊完就结束了。

评价: 还挺务实的,能够在他的引导下解决一些问题(可能说的悟性吧),然后说还挺年轻的,96年,还有很多机会 我说谢谢,谢谢您今天给我讲了很多新的东西(说实话,真的还想问问他的联系方式以后能多多请教,但是想到面试官阅人无数,每一个都想加好友,肯定也处理不过来,万一面试官不方便什么的,场面很尴尬,所以就没问)。第二场面试就这样结束了,5点55,同样是70分钟! 根据面试官对我的评价和我的回答,我觉得还是有可能有三面的,但是后面我问如果有第三轮面试,会在什么时候,面试官告诉我可能就是过一会儿,不过,也可能是周一,今天快下班了。这时候觉得可能还会有,起码没有很糟糕的评价,但是我估计更大的可能性是在下周,因为6点多了,应该不会打来了。 事实证明,我的推想是不对的。电话响了。

三面

6点16分,三面开始。 三周后,百度梦醒了,三面没过,不想写了