写给小白的android基础面试笔试题(二)
序言:找工作绝逼是一件非常痛苦的事,尤其是年底找工作,当然了,这其中大部分原因肯定是因为LZ只是个没有经验的,还没毕业的大学生。今天看到一句掉渣天的骂人的话:“你TM就是一个没有对象的野指针”,哈哈哈!扯淡就到这,下面进入正题(如有错误,请及时纠正)。
写给小白的android基础面试笔试题(一)
写给小白的android基础面试笔试题(二)
写给小白的android基础面试笔试题(三)
注:每个问题的最后都会有正确答案(粗体的就是正确答案)
1、请简述一下你对Handler机制的理解?
答:这道题呢不管是在笔试中也好面试中也好,会被经常拿来问,因为这里面涉及到的知识点甚多,这里你就要回答什么是Handler机制,有何作用,然后面试官肯定还会问,Handler的几个老朋友Message,Looper以及MessageQueue和Handler有什么关系。所以这里你必须要搞懂这个:
答:首先呢,Handler机制是Android中异步消息处理机制,也就是异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。而他们之间的关系就是,消息循环Looper负责创建一个消息队列MessageQueue,而消息队列MessageQueue是消息Message的容器,也就是装消息Message的地方,然后进入一个无限循环体不断从该消息队列MessageQueue中读取消息,而消息的创建者和处理者就是一个或多个Handler。
2、android项目目录中res目录和assets目录的区别?
答:这是笔试题中遇到的,不止一道关于res目录和assets目录的,下面我们会好好讲解这两个目录。
答:首先呢,这两个目录的相同点就是目录下的文件都不会被编译为二进制文件,这两个文件夹下的内容会保持原样;不同点是:
(1)、res:该目录下的文件都会自动在R.java中生成资源id,可以通过getResources的相关方法结合其他类进行访问;而且res目录下不能随意创建文件夹。
(2)、assets:该目录支持创建任意深度的文件夹,并且目录下的文件不会在R.java中生成资源id,必须使用/assets开始的相对路径按照文件的方式进行访问。可以使用AssetManager 结合其他类进行访问。
不过这里需要注意一点,assets目录下不仅仅可以存放音频、字体等较大的文件,还可以存放大的图片
3、对Java的设计模式了解多少?用过哪些设计模式(我回答了下面几个,然后他又发问了)?说说你对单例模式的了解?
答:OMG!我的第一反应就是这个问题问的就有点深了,后来想想其实也还好,只是问你用过的一些,然后我就讲了几个我用到过的,单例模式、适配器模式、建造者模式、观察者模式,好像没了,像其他的一些工厂模式、策略模式、访问者模式什么的都只是听过,可能用到过,但是自己不知道而已>_<
答:首先解释一下单例模式:为了确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式的特点:
(1)、单例模式只能有一个实例。
(2)、单例类必须自己创建自己的唯一实例。
(3)、单例类必须给所有其他对象提供这一实例。
单例模式通常在一些或多或少具有资源管理器的功能的地方使用,比如说线程池、缓存、对话框等等。我觉得对于像我这样一个初级的猿猿来说懂这些应该就够了,当然了,你还得知道单例模式的几种写法,看到这里的童鞋,请移步JAVA设计模式之单例模式,里面有比较详细的讲解
4、你用过Gradle吗?(呃!我用的是studio开发,所以gradle还是经常用的)请说说你知道的gradle的一些用法(配置)?
答:这道题,说真的,我刚开始有点懵逼,我之前看到过讲的很详细的关于Gradle的博文,但是可悲的是我没有仔细去研究;相信大家都知道Maven吧!Gradle的功能比Maven还要强大。然后你可以先介绍一下Gradle是个啥,然后再讲讲你知道的一些Gradle配置,然后基本就差不多了。
Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。在Android Studio中,我们可以使用Gradle来完成APK的编译及打包工作。使用Gradle的原因:
(1)、使用领域专用语言(Domain Specific Language)来描述和处理构建逻辑。
(2)、基于Groovy。DSL可以混合各种声明元素,用代码操控这些DSL元素达到逻辑自定义。
(3)、支持已有的Maven或者Ivy仓库基础建设。
(4)、非常灵活,允许使用best practices,并不强制让你遵照它的原则来。
(5)、其它插件时可以暴露自己的DSL和API来让Gradle构建文件使用。
(6)、允许IDE集成,是很好的API工具。
在Android Studio中,你可以在Gradle中配置以下内容:
(1)、配置插件及插件的属性
(2)、配置远程仓库,像jcenter和maven
(3)、配置所依赖的第三方库,jar包等
(4)、配置多渠道打包的信息
(5)、配置应用的签名信息,编译版本信息等等
上面的这些都是比较基础的一些,想了解更详细的请移步Gradle从入门到了解
5、Activity的四种状态是什么,分别对应的Activity的生命周期是什么?
答:这是一道笔试大题,这也是一道比较基础而且又有考点的题目,有些时候你的知道在哪个状态该干些什么事,在生命周期切换的时候可以做哪些事。我当时写的比较糙,没怎么写全,可能自己也有点不记得了,之前看郭神的第一行代码中写的比较详细。
Activity的四种状态分别是:
(1)、运行状态(Running):当一个Activity在栈顶,它是可视的、有焦点、可接受用户输入的。Android试图尽最大可能保持它活动状态,杀死其它Activity来确保当前活动Activity有足够的资源可使用。当另外一个Activity被激活,这个将会被暂停。
(2)、暂停状态(Paused):在很多情况下,你的Activity可视但是它没有焦点,换句话说它被暂停了。有可能原因是一个透明或者非全屏的Activity被激活。当被暂停,一个Activity仍会当成活动状态,只不过是不可以接受用户输入。在极特殊的情况下,Android将会杀死一个暂停的Activity来为活动的Activity提供充足的资源。当一个Activity变为完全隐藏,它将会变成停止。
(3)、停止状态(Stopped):当一个Activity不是可视的,它“停止”了。这个Activity将仍然在内存中保存它所有的状态和会员信息。尽管如此,当其它地方需要内存时,它将是最有可能被释放资源的。当一个Activity停止后,一个很重要的步骤是要保存数据和当前UI状态。一旦一个Activity退出或关闭了,它将变为销毁状态。
(4)、销毁状态(Destroyed):在一个Activity被杀死后和被装载前,它是销毁状态的。被销毁Acitivity被移除Activity栈,并且需要在显示和可用之前重新启动它。
这四种状态还是比较容易理解的,下面我们来看一下和生命周期串联起来:
(1)、运行状态:进入Activity,onCreate—>onStart—>onResume,这时候Activity就处于运行状态了。
(2)、暂停状态:在当前Activity中打开一个半透明的Activity然后再返回来,onPause—>onRestart—>onStart—>onResume,不会运行onStop,这样Activity就经历了暂停状态,请注意暂停状态和停止状态的区别。
(3)、停止状态:当前Activity跳转到另外一个Activity然后再返回来,onPause—>onStop—>onRestart—>onStart—>onResume,这样Activity就经历了暂停状态、停止状态,然后又回到了运行状态。
(4)、销毁状态:当前Activity按返回键,onPause—>onStop—>onDestroy,这时候Activity就被销毁了,等到下一次被运行。
6、请说说你是怎么优化ListView的?
答:这道题说简单也不简单,说难应该也不算难,如果你搞懂这里面的原理就一点不难,就我个人而言,没做过的就是分页加载的逻辑。
优化一:在Adapter中的getView方法中使用ConvertView,即ConvertView的复用,不需要每次都inflate一个View出来,这样既浪费时间,又浪费内存。
优化二:使用ViewHolder,不要在getView方法中写findViewById方法,因为getView方法会执行很多遍,这样也可以节省时间,节约内存。
优化三:使用分页加载,讲真实际开发中,ListView的数据肯定不止几百条,成千上万条数据你不可能一次性加载出来,所以这里需要用到分页加载,一次加载几条或者十几条,但是如果数据量很大的话,像qq,微信这种,如果顺利加载到最后面的话,那么你的list中也会有几万甚至几十万的数据,这样可能也会导致OOM,所以你的数据集List中也不能有那么多数据,所以每加载一页的时候你可以覆盖前一页的数据。
优化四:如果数据当中有图片的话,使用第三方库来加载(也就是缓存),如果你的能力强大到能自己维护的话,那也不是不可以。
优化五:当你手指在滑动列表的时候,尽可能的不加载图片,这样的话滑动就会更加流畅。
差不多回答个这么些也就可以了,如果你还有更厉害的,请私戳回复我一下,咱们共同进步
7、说说你对Java的线程机制的理解?
答:首先呢这道题是在电话面试的时候面试官问的,线程机制如果要讲的话会很深,可是LZ回答的时候只是讲了一下线程的概念,然后按照自己平常的使用方法讲了一下,如果你知道的深的话你可以拿出来装装逼了。
首先线程是程序运行的最小单元,有时候被称为轻量级进程。线程的创建方法有两种,第一,继承Thread类,然后实现run方法即可;第二,使用内部类实现Runnable接口亦可。
第一种:
public class A extends Thread {
@Override
public void run() {
super.run();
}
}
使用:
new A().start();
第二种:
public class B implements Runnable {
@Override
public void run() {
}
}
使用:
B b=bew B();
new Thread(b).start();
然后可以再说说线程的生命周期,因为这也是一个比较重要的知识点:
(1)、新建状态:用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态
注意:不能对已经启动的线程再次调用start()方法,否则会出现Java.lang.IllegalThreadStateException异常。
(2)、就绪状态:处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,处于线程就绪队列,等待系统为其分配CPU
(3)、运行状态:处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
(4)、阻塞状态:处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态。
(5)、死亡状态:当线程的run()方法执行完,或者被强制性地终止,就认为它死去
注意:线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
在Android中,用到线程的地方一般是网络请求,大文件的加载等耗时的操作,用到线程的地方大多可能会用到handler,这个上面有讲到。
8、说说你对Java的GC机制的理解?GC机制与纯计数方式的自动内存管理相比有什么优势?
答:这道题也算是比较经典的了,我知道的也比较浅显,但是GC机制也很重要,所以,正在看这个的你们一定要重视,可以去找找Java中关于JVM的书籍或者博客去看一看Java中JVM的原理,因为这玩意儿真的很重要。
GC机制算得上是Java语言中一个比较优秀的机制了,它能自动管理内存的回收,相对于C/C++来说,程序员不必手动调用方法来释放内存。首先你得了解JVM内存的组成结构,JVM是由堆、栈、本地方法栈、方法区等部分组成,至于这些玩意是什么,详细可以参考深入了解Java GC的工作原理;当程序员使用new关键字创建对象时,GC就开始监控这个对象的地址,大小以及使用情况。一般来说,GC是采用有向图的方式来记录和管理堆中所有的对象。GC与纯计数方式的自动内存管理相比:
(1)、最基本的纯引用计数方式的自动内存管理可以做到实时释放死对象,但却无法处理存在循环引用的对象图的释放。
(2)、最基本的纯引用计数方式对引用计数器的操作非常频繁,这里有额外开销,至于是否严重到成问题就看具体应用的可忍受程度。
(3)、如果选用GC来实现自动内存管理,它是不显式维护对象的引用计数的,也就没有“引用计数到0”的概念。所以基于GC的JVM或其它语言的运行时环境自然不会“引用计数到0就释放对象”。
(4)、引用计数方式其实也有经典的卡顿情况。例子之一就是一个对象个数很多、引用链很长的对象图假如只是被一个引用而留活,那么那个引用一死就会引发大量对象扎堆释放(但却不是“批量释放”,开销不同),这一样会引起卡顿。
另外为了保证GC能够在不同平台实现的问题,Java规范对GC的很多行为都没有进行严格的规定。例如,对于采用什么类型的回收算法、什么时候进行回收等重要问题都没有明确的规定。因此,不同的JVM的实现者往往有不同的实现算法。这也给Java程序员的开发带来行多不确定性。
9、说说你对Java中几种引用的理解?
答:这道题对我来说并不陌生,因为每次看面试题的时候都会经常看到,但是我又真的没有使用过这些引用,所以我就回答了这几个引用的概念,面试官后面也问了在什么情况下会使用这些。
首先你得了解这四种引用的概念:
(1)、强引用:如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象
(2)、软引用:在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收。
(3)、弱引用:具有弱引用的对象拥有的生命周期更短暂。因为当 JVM 进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象
(4)、虚引用:顾名思义,就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。
讲到这里,LZ又要建议你去看看JVM了,这是一个比较深的知识点。至于在Android项目中在哪里能使用,我只知道内存优化的使用,比如在你初始化Handler的时候,周围的颜色会变黄,这个是编译器提示的,有可能会发生内存泄漏这个问题的,所以你可以给Handler加上软引用来避免这个问题。其他的也一样,如果你不想某个东西造成内存泄漏,你就可以给它加上软引用,让GC来消灭它
10、int和Integer的区别,String和StringBuilder、StringBuffer的区别?
答:这道题,首先呢你得知道Java的8种基本的数据类型,byte,short,int,long,double,float,char,boolean,他们都有与之对应的对象。至于后面那个问题,LZ建议你去看看源码,这样的话你就能比较深刻的理解。
首先呢int和Integer前者是基本数据类型,后者是一个对象,这没什么好说的,后面的String和另外两者的区别是,String是字符串常量,另外两个是字符串变量,再说白一点,String是不会变的,而后面的是可变的,这里可能就有人要问了,我在使用字符串想加的时候,String不是也变了吗?NoNoNo,这里说的不可变是你给他赋初始值之后String就不会变了,你之后用加法的过程中它又创建了一个新的String对象来操作你的加法,原本的初始的String的值仍然没有改变;然后我们说说后面两者的区别,StringBuilder是线程不安全的,因此它操作字符串会比较快,而StringBuffer是线程安全的,所以它操作字符串会比较慢,关于线程安全这方面,你可以去看看这两个的源码。另外你还得知道怎样合理的使用这三者:
(1)、String:操作少量数据的时候使用。
(2)、StringBuilder:单线程操作字符串缓冲区下操作大量数据。
(3)、StringBuffer:多线程操作字符串缓冲区下操作大量数据。
在此感谢上面引用到的博客的博主!!!!
今天LZ终于结束了这20多天寻找工作的旅途,希望我的博客能够帮到还在找工作的小白,LZ给你们的建议仍然是不要放弃,真的喜欢Android,就做下去吧!
公众号:Android技术经验分享