Android面试常见问题

                                     JAVA部分:

1.equals与==的区别:

==是判断两个变量或实例是不是指向同一个内存空间equals是判断两个变量或实例所指向的内存空间的值是不是相同。

2. Hashcode的作用,与equal有什么区别

为了鉴定2个对象是否相等的,java集合中有list和set两类,其中set不允许元素重复实现,那个这个不允许重复实现的方法,如果用equal去比较的话,如果存在1000个元素,你new一个新的元素出来,需要去调用1000次equal去逐个和他们比较是否是同一个对象,这样会大大降低效率。hashcode实际上是返回对象的存储地址,如果这个位置上没有元素,就把元素直接存储在上面,如果这个位置上已经存在元素,这个时候才去调用equal方法与新元素进行比较,相同的话就不存了,散列到其他地址上。

3. String、StringBuffer与StringBuilder的区别

String类型和StringBuffer类型的主要性能区别其实在于String是不可变的对象,StringBuffer和StringBuilder底层是char[]数组实现的。StringBuffer是线程安全的,而StringBuilder是线程不安全的。

4. Override和Overload的含义去区别

Overload顾名思义是重新加载,它可以表现类的多态性,。函数里面可以有相同的函数名,但是参数名、返回值、类型不能相同;或者说可以改变参数、类型、返回值但是函数名字依然不变。Override顾名思义就是ride(重写)的意思,在子类继承父类的时候子类中可以定义某方法与其父类有相同的名称和参数,当子类在调用这一函数时自动调用子类的方法,而父类相当于被覆盖(重写)了。

5.抽象类和接口的区别

一个类只能继承单个类,但是可以实现多个接口接口强调特定功能的实现,而抽象类强调所属关系抽象类中的所有方法并不一定要是抽象的,你可以选择在抽象类中实现一些基本的方法。而接口要求所有的方法都必须是抽象的。

6.wait()和sleep()的区别

(1)sleep来自Thread类,wait来自Object类

(2)调用sleep()方法的过程中,线程不会释放对象锁。而 调用wait方法线程会释放对象锁

(3)sleep睡眠后不出让系统资源,wait让出系统资源其他线程可以占用CPU

(4)sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒

6.JAVA多态的实现原理

抽象的来讲,多态的意思就是同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)实现的原理是动态绑定,程序调用的方法在运行期才动态绑定.

7.怎么判断一个对象是否需要收集?

(1)引用计数(最简单古老的方法):将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放

(2)对象引用遍历(现在大多数jvm使用的方法):对象引用遍历从一组对象开始,沿着整个对象图上的每条链接,递归确定可到达的对象。如果某对象不能从这些根对象的一个到达,则将它作为垃圾收集.

8.Java的四种引用的区别

(1)强引用:如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出OutOfMemoryError错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象

(2)软引用:在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收。

(3)弱引用:具有弱引用的对象拥有的生命周期更短暂。因为当JVM进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象

(4)虚引用:顾名思义,就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。

9.ArrayList、LinkedList、Vector的区别

(1)ArrayList和Vector底层是采用数组方式存储数据,Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差

(2)LinkedList使用双向链表实现存储,随机存取比较慢

(3)HashMap的底层源码实现:当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。

10.HashMap和 HashTable 的区别

HashTable比较老,是基于Dictionary类实现的,HashTable则是基于Map接口实现的。HashTable是线程安全的,HashMap则是线程不安全的。HashMap可以让你将空值作为一个表的条目的key或value。

11.什么是线程池,线程池的作用是什么

线程池就是开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

java线程池的作用:a.重用存在的线程,减少对象创建、消亡的开销,性能佳。b.可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。c.提供定时执行、定期执行、单线程、并发数控制等功能。

                                                                Android部分

1.绘制Activity生命周期流程图



2.内存不足时系统会杀掉后台的Activity,若需要进行一些临时状态的保存,在哪个方法进行?

Activity的onSaveInstanceState()和onRestoreInstanceState()并不是生命周期方法,它们不同于onCreate()、onPause()等生命周期方法,它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity,onSaveInstanceState()会被调用。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。除非该activity是被用户主动销毁的,通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。

3.onSaveInstanceState()被执行的场景有哪些:

系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,因此系统都会调用onSaveInstanceState(),让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则

1.当用户按下HOME键时

2.长按HOME键,选择运行其他的程序时

3.锁屏时

4.从activity A中启动一个新的activity时

5.屏幕方向切换时

4.介绍Activity的几中启动模式

(1)standard

(2)SingleTop

(3)SingleTask

(4)SingleInstance

5.注册Service需要注意什么

Service还是运行在主线程当中的,所以如果需要执行一些复杂的逻辑操作,最好在服务的内部手动创建子线程进行处理,否则会出现UI线程被阻塞的问题

6.Service与Activity怎么实现通信

方法一:

1.添加一个继承Binder的内部类,并添加相应的逻辑方法

2.重写Service的onBind方法,返回我们刚刚定义的那个内部类实例

方法二:

通过BroadCast(广播)的形式 当我们的进度发生变化的时候我们发送一条广播,然后在Activity的注册广播接收器,接收到广播之后更新视图.

7.IntentService与Service的区别

IntentService是Service的子类,是一个异步的,会自动停止的服务,很好解决了传统的Service中处理完耗时操作忘记停止并销毁Service的问题.

IntentService会创建独立的worker线程来处理所有的Intent请求;会创建独立的worker线程来处理onHandleIntent()方法实现的代码,无需处理多线程问题;所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service;为Service的onBind()提供默认实现,返回null;为Service的onStartCommand提供默认实现,将请求Intent添加到队列中;

IntentService不会阻塞UI线程,而普通Serveice会导致ANR异常

Intentservice若未执行完成上一次的任务,将不会新开一个线程,是等待之前的任务完成后,再执行新的任务,等任务完成后再次调用stopSelf().

8.介绍Handle的机制

·Handler通过调用sendmessage方法把消息放在消息队列MessageQueue中,Looper负责把消息从消息队列中取出来,重新再交给Handler进行处理,三者形成一个循环

·通过构建一个消息队列,把所有的Message进行统一的管理,当Message不用了,并不作为垃圾回收,而是放入消息队列中,供下次handler创建消息时候使用,提高了消息对象的复用,减少系统垃圾回收的次数

·每一个线程,都会单独对应的一个looper,这个looper通过ThreadLocal来创建,保证每个线程只创建一个looper,looper初始化后就会调用looper.loop创建一个MessageQueue,这个方法在UI线程初始化的时候就会完成,我们不需要手动创建.

9.ListView卡顿的原因与性能优化,越多越好

(1)重用converView:通过复用converview来减少不必要的view的创建,另外Infalte操作会把xml文件实例化成相应的View实例,属于IO操作,是耗时操作。

(2)减少findViewById()操作:将xml文件中的元素封装成viewholder静态类,通过converview的setTag和getTag方法将view与相应的holder对象绑定在一起,避免不必要的findviewbyid操作

(3)避免在getView方法中做耗时的操作:例如加载本地Image需要载入内存以及解析Bitmap,都是比较耗时的操作,如果用户快速滑动listview,会因为getview逻辑过于复杂耗时而造成滑动卡顿现象。用户滑动时候不要加载图片,待滑动完成再加载,可以使用这个第三方库glide

(4)Item的布局层次结构尽量简单,避免布局太深或者不必要的重绘

(5)尽量能保证Adapter的hasStableIds()返回true这样在notifyDataSetChanged()的时候,如果item内容并没有变化,ListView将不会重新绘制这个View,达到优化的目的

(6)在一些场景中,ScollView内会包含多个ListView,可以把listview的高度写死固定下来。由于ScollView在快速滑动过程中需要大量计算每一个listview的高度,阻塞了UI线程导致卡顿现象出现,如果我们每一个item的高度都是均匀的,可以通过计算把listview的高度确定下来,避免卡顿现象出现.

(7)使用RecycleView代替listview:每个item内容的变动,listview都需要去调用notifyDataSetChanged来更新全部的item,太浪费性能了。RecycleView可以实现当个item的局部刷新,并且引入了增加和删除的动态效果,在性能上和定制上都有很大的改善

(8)ListView中元素避免半透明:半透明绘制需要大量乘法计算,在滑动时不停重绘会造成大量的计算,在比较差的机子上会比较卡。在设计上能不半透明就不不半透明。实在要弄就把在滑动的时候把半透明设置成不透明,滑动完再重新设置成半透明。

(9)尽量开启硬件加速:硬件加速提升巨大,避免使用一些不支持的函数导致含泪关闭某个地方的硬件加速。当然这一条不只是对ListView。

10.如何使用JNI

JAVA中声明native方法如private native String printJNI(String inputStr);

使用javah工具生成.h头文件这时候头文件中就会自动生成对应的函数JNIEXPORT jstring JNICALL Java_com_wenming_HelloWorld_printJNI

实现JNI原生函数源文件,新建HelloWorld.c文件,对刚才自动生成的函数进行具体的逻辑书写,例如返回一个java叫做HelloWorld的字符串等

编译生成动态链接so文件**

Java中调用Sysytem.load方法把刚才的so库加载进来,就可以调用native方法了.

11.什么OOM?

OOM全称是Out Of Merrory,Android系统的每一个应用程序都设置一个硬性的Dalvik Heap Size最大限制阈值,如果申请的内存资源超过这个限制,系统就会抛出OOM错误.

12.内存泄漏有哪些场景以及解决方法

(1)类的静态变量持有大数据对象,静态变量长期维持到大数据对象的引用,阻止垃圾回收。

(2)非静态内部类存在静态实例.非静态内部类会维持一个到外部类实例的引用,如果非静态内部类的实例是静态的,就会间接长期维持着外部类的引用,阻止被回收掉。

(3)资源对象未关闭资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们, 以便它们的缓冲及时回收内存。解决办法:比如SQLiteCursor, 如果我们没有关闭它,系统在回收它时也会关闭它,但是这样的效率太低了。 因此对于资源性对象在不使用的时候,应该调用它的close()函数,将其关闭掉,然后才置为null.在我们的程序退出时一定要确保我们的资源性对象已经关闭。

(4)Handler内存泄漏Handler作为内部类存在于Activity中,但是Handler生命周期与Activity生命周期往往并不是相同的,比如当Handler对象有Message在排队,则无法释放,进而导致本该释放的Acitivity也没有办法进行回收。解决办法:声明handler为static类,这样内部类就不再持有外部类的引用了,就不会阻塞Activity的释放

12.如何避免OOM问题的出现

(1)使用更加轻量的数据结构.例如,我们可以考虑使用ArrayMap/SparseArray而不是HashMap等传统数据结构。通常的HashMap的实现方式更加消耗内存,因为它需要一个额外的实例对象来记录Mapping操作。

(2)避免在Android里面使用Enum

(3)减小Bitmap对象的内存占用.Bitmap是一个极容易消耗内存的大胖子,减小创建出来的Bitmap的内存占用可谓是重中之重.

(4)Bitmap对象的复用.缩小Bitmap的同时,也需要提高BitMap对象的复用率,避免频繁创建BitMap对象.

(5)使用更小的图片.在涉及给到资源图片时,我们需要特别留意这张图片是否存在可以压缩的空间,是否可以使用更小的图片。尽量使用更小的图片不仅可以减少内存的使用,还能避免出现大量的InflationException。

(6)StringBuilder在有些时候,代码中会需要使用到大量的字符串拼接的操作,这种时候有必要考虑使用StringBuilder来替代频繁的“+”。

(7)避免在onDraw方法里面执行对象的创建.类似onDraw等频繁调用的方法,一定需要注意避免在这里做创建对象的操作,因为他会迅速增加内存的使用,而且很容易引起频繁的gc,甚至是内存抖动。

(8)避免对象的内存泄露.

13.什么是ANR?是由于什么原因引起的?

ANR全称Application Not Responding,意思就是程序未响应。如果一个应用无法响应用户的输入,系统就会弹出一个ANR对话框,用户可以自行选择继续等待亦或者是停止当前程序。一旦出现下面两种情况,则弹出ANR对话框

·应用在5秒内未响应用户的输入事件(如按键或者触摸)

·BroadcastReceiver未在10秒内完成相关的处理

原因:

(1)主线程中存在耗时的计算

(2)主线程被IO操作(从4.0之后网络IO不允许在主线程中)阻塞。

(3)主线程中错误的操作,比如Thread.wait或者Thread.sleep等

如何避免ANR问题:

(1)使用AsyncTask处理耗时IO操作。

(2)使用Handler处理子线程结果,而不是使用Thread.wait()或者Thread.sleep()来阻塞主线程。

(3)Activity的onCreate和onResume回调中尽量避免耗时的代码

(4)BroadcastReceiver中onReceive方法的操作时间在10秒之内

14.数据持久化的四种方式有哪些?

(1)文件存储:通过java.io.FileInputStream和java.io.FileOutputStream这两个类来实现对文件的读写,java.io.File类则用来构造一个具体指向某个文件或者文件夹的对象。

(2)SharedPreferences:SharedPreferences是一种轻量级的数据存储机制,他将一些简单的数据类型的数据,包括boolean类型,int类型,float类型,long类型以及String类型的数据,以键值对的形式存储在应用程序的私有Preferences目录(/data/data/<包名>/shared_prefs/)中,这种Preferences机制广泛应用于存储应用程序中的配置信息。

(3)SQLite数据库:当应用程序需要处理的数据量比较大时,为了更加合理地存储、管理、查询数据,我们往往使用关系数据库来存储数据。Android系统的很多用户数据,如联系人信息,通话记录,短信息等,都是存储在SQLite数据库当中的,所以利用操作SQLite数据库的API可以同样方便的访问和修改这些数据。

(4)ContentProvider:主要用于在不同的应用程序之间实现数据共享的功能,不同于sharepreference和文件存储中的两种全局可读写操作模式,内容提供其可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险.

你可能感兴趣的:(Android面试常见问题)