自整理Android面试必备

Java基础(1 - 15)

2018-03-26  本文已影响13人  街头客

目录

1,equals与==的区别;
2,静态内部类、内部类、匿名内部类,为什么内部类会持有外部类的引用?持有的引用是this?还是其它?
3,String、StringBuffer与StringBuilder的区别;
4,Java的四种引用的区别;
5,jvm垃圾回收机制;
6,HashMap和 HashTable 的区别;
7,hashMap是怎样实现的;
8,数据结构中用于存储数据的有哪些;
9,wait()和sleep()的区别;
10,若Activity已经销毁,此时AsyncTask执行完并且返回结果,会报异常吗?
11,Android 线程间通信有哪几种方式(重要);
12,Android进程间通信的几种方式(重要);
13,AsyncTask的内部实现原理;
14,接口的意义;
15,抽象类的意义;

1,equals与==的区别:

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

2,静态内部类、内部类、匿名内部类,为什么内部类会持有外部类的引用?持有的引用是this?还是其它?

静态内部类:使用static修饰的内部类;

内部类:就是在某个类的内部又定义了一个类,内部类所嵌入的类称为外部类;

匿名内部类:使用new生成的内部类,而且只能使用一次;


TestClass类

这时候你可以使用匿名类继承这个类,定义一个方法callParentTest(),在这个方法体内调用super.test(),最后调用这个callParentTest()即可。


new 匿名类

因为内部类的产生依赖于外部类,持有的引用是类名.this;

3,String、StringBuffer与StringBuilder的区别:

String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象;

StringBuffer和StringBuilder底层是 char[]数组实现的;

StringBuffer是线程安全的,而StringBuilder是线程不安全的;

【转发】https://www.cnblogs.com/su-feng/p/6659064.html

String S1 = "This is only a" + "simple" + " test";
StringBuffer Sb = new StringBuffer("This is only a").append("simple").append("test");

你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个 String S1 = “This is only a” + “ simple” + “test”; 其实就是: String S1 = “This is only a simple test”; 所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如: String S2 = “This is only a”; String S3 = “ simple”; String S4 = “ test”; String S1 = S2 +S3 + S4; 这时候 JVM 会规规矩矩的按照原来的方式去做。

操作速度: StringBuilder > StringBuffer > String。

PS: 总结一下

String:适用于少量的字符串操作的情况

StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况

StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况(加锁,线程安全)

4,Java的四种引用的区别:

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

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

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

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

PS:四种引用类型的比较

四种引用类型的比较
【转发】https://blog.csdn.net/rabbit_in_android/article/details/49833749

PS: 总结一下

WeakReference与SoftReference都可以用来保存对象的实例引用,这两个类与垃圾回收有关。

WeakReference保存的对象实例可以被GC回收掉。
这个类通常用于在某处保存对象引用,而又不干扰该对象被GC回收,通常用于Debug、内存监视工具等程序中。
因为这类程序一般要求即要观察到对象,又不能影响该对象正常的GC过程。
最近在JDK的Proxy类的实现代码中也发现了Weakrefrence的应用,
Proxy会把动态生成的Class实例暂存于一个由Weakrefrence构成的Map中作为Cache。

SoftReference是强引用,它保存的对象实例,除非JVM即将OutOfMemory,否则不会被GC回收。
这个特性使得它特别适合设计对象Cache。
对于Cache,我们希望被缓存的对象最好始终常驻内存,但是如果JVM内存吃紧,
为了不发生OutOfMemoryError导致系统崩溃,必要的时候也允许JVM回收Cache的内存,
待后续合适的时机再把数据重新Load到Cache中。这样可以系统设计得更具弹性。

5,jvm垃圾回收机制:

标记回收法:遍历对象图并且记录可到达的对象,以便删除不可到达的对象,一般使用单线程工作并且可能产生内存碎片。

标记-压缩回收法:前期与第一种方法相同,只是多了一步,将所有的存活对象压缩到内存的一端,这样内存碎片就可以合成一大块可再利用的内存区域,提高了内存利用率。

复制回收法:把现有内存空间分成两部分,gc运行时,它把可到达对象复制到另一半空间,再清空正在使用的空间的全部对象。这种方法适用于短生存期的对象,持续复制长生存期的对象则导致效率降低。

分代回收发:把内存空间分为两个或者多个域,如年轻代和老年代,年轻代的特点是对象会很快被回收,因此在年轻代使用效率比较高的算法。当一个对象经过几次回收后依然存活,对象就会被放入称为老年的内存空间,老年代则采取标记-压缩算法。

【转发】https://www.cnblogs.com/fefjay/p/6297340.html

PS:
a,内存调优:
JVM内存调优,主要是减少GC的频率和减少Full GC的次数,Full GC的时候会极大地影响系统的性能。所以在此基础上,更加要关注会导致Full GC的情况。

b,容易导致Full GC的情况:
年老代空间不足
1)分配足够大空间给old gen,年老代空间小,从而更容易诱发Full GC。
2)避免直接创建过大对象或者数组,否则会绕过年轻代直接进入年老代。
3)应该使对象尽量在年轻代就被回收,或待得时间尽量久,避免过早的把对象移进年老代。

方法区的永久代空间不足
1)分配足够大空间给永久代。
2)避免创建过多的静态对象。

被显示调用System.gc()
通常情况下不要显示地触发GC,让JVM根据自己的机制实现。

6,HashMap和 HashTable 的区别:

HashTable比较老,是基于Dictionary 类实现的,HashMap 则是基于 Map接口实现的;

HashTable 是线程安全的, HashMap 则是线程不安全的;

HashMap可以让你将空值作为一个表的条目的key或value;

7,hashMap是怎样实现的:

哈希表:由数组+链表组成的。

当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标)。

如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。

如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。

8,数据结构中用于存储数据的有哪些:

数组

数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小;数组的特点是:寻址容易,插入和删除困难。

链表

链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大。链表的特点是:寻址困难,插入和删除容易。

9,wait()和sleep()的区别:

sleep来自Thread类,和wait来自Object类;

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

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

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

10,若Activity已经销毁,此时AsyncTask执行完并且返回结果,会报异常吗?

若AsyncTask正在执行,则会报 view not attached to window manager 异常。

这同样也是生命周期的问题,在 Activity 的onDestroy()方法中调用AsyncTask.cancel方法,让二者的生命周期同步。

11,Android 线程间通信有哪几种方式(重要):

共享内存(变量);

文件,数据库;

Handler;

Java 里的 wait(),notify(),notifyAll();

12,Android进程间通信的几种方式(重要):

Bundle/Intent传递数据;
可传递基本类型,String,实现了Serializable或Parcellable接口的数据结构。Serializable是Java的序列化方法,Parcellable是Android的序列化方法,前者代码量少(仅一句),但I/O开销较大,一般用于输出到磁盘或网卡;后者实现代码多,效率高,一般用户内存间序列化和反序列化传输。

共享缓存文件;

AIDL方式;

Messenger;
双方用Messenger来发送数据,用Handler来处理数据。Messenger处理数据依靠Handler,所以是串行的,也就是说,Handler接到多个message时,就要排队依次处理。

ContentProvider;
主要用来为其他APP提供数据,可以说天生就是为进程通信而生的。

Socket;
使用Socket来请求端口,连通后就可以进行通信。

13,AsyncTask的内部实现原理:

a,AsyncTask 内部也是 Handler 机制来完成的;


InternalHandler

它内部实例化了一个静态的自定义类 InternalHandler,这个类是继承自 Handler 的,在这个自定义类中绑定了一个叫做 AsyncTaskResult 的对象,每次子线程需要通知主线程,就调用 sendToTarget 发送消息给 handler。然后在 handler 的 handleMessage 中 AsyncTaskResult 根据消息的类型不同(例如 MESSAGE_POST_PROGRESS 会更新进度条,MESSAGE_POST_CANCEL 取消任务)而做不同的操作,值得一提的是,这些操作都是在UI线程进行的,意味着,从子线程一旦需要和 UI 线程交互,内部自动调用了 handler 对象把消息放在了主线程了。


主线程、子线程

b,只不过 Android 提供了执行框架来提供线程池来执行相应地任务,因为线程池的大小问题,所以 AsyncTask 只应该用来执行耗时时间较短的任务;


线程数量

比如 HTTP 请求,大规模的下载和数据库的更改不适用于 AsyncTask,因为会导致线程池堵塞,没有线程来执行其他的任务,导致的情形是会发生 AsyncTask 根本执行不了的问题。

【转发】https://blog.csdn.net/yuqi007163/article/details/53673486
【转发】https://blog.csdn.net/cxmscb/article/details/51863748

PS:总结一下:

14,接口的意义:

规范,扩展,回调。

15,抽象类的意义:

上一篇 下一篇

猜你喜欢

热点阅读