Java基础(1 - 15)
目录
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生成的内部类,而且只能使用一次;
![](https://img.haomeiwen.com/i5221864/b10ce2faee532371.png)
这时候你可以使用匿名类继承这个类,定义一个方法callParentTest(),在这个方法体内调用super.test(),最后调用这个callParentTest()即可。
![](https://img.haomeiwen.com/i5221864/19ce03b62b2e535f.png)
因为内部类的产生依赖于外部类,持有的引用是类名.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://img.haomeiwen.com/i5221864/27a116005a58f89a.png)
【转发】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 机制来完成的;
![](https://img.haomeiwen.com/i5221864/adbc9a34792fda12.png)
它内部实例化了一个静态的自定义类 InternalHandler,这个类是继承自 Handler 的,在这个自定义类中绑定了一个叫做 AsyncTaskResult 的对象,每次子线程需要通知主线程,就调用 sendToTarget 发送消息给 handler。然后在 handler 的 handleMessage 中 AsyncTaskResult 根据消息的类型不同(例如 MESSAGE_POST_PROGRESS 会更新进度条,MESSAGE_POST_CANCEL 取消任务)而做不同的操作,值得一提的是,这些操作都是在UI线程进行的,意味着,从子线程一旦需要和 UI 线程交互,内部自动调用了 handler 对象把消息放在了主线程了。
![](https://img.haomeiwen.com/i5221864/1aa0fd037211d512.png)
b,只不过 Android 提供了执行框架来提供线程池来执行相应地任务,因为线程池的大小问题,所以 AsyncTask 只应该用来执行耗时时间较短的任务;
![](https://img.haomeiwen.com/i5221864/c587d6333c876607.png)
比如 HTTP 请求,大规模的下载和数据库的更改不适用于 AsyncTask,因为会导致线程池堵塞,没有线程来执行其他的任务,导致的情形是会发生 AsyncTask 根本执行不了的问题。
【转发】https://blog.csdn.net/yuqi007163/article/details/53673486
【转发】https://blog.csdn.net/cxmscb/article/details/51863748
PS:总结一下:
- 由于 Handler 需要和主线程交互,而 Handler 又是内置于 AsyncTask 中的,所以,AsyncTask 的创建必须在主线程。
- AsyncTaskResult 的 doInBackground(mParams)方法执行异步任务运行在子线程中,其他方法运行在主线程中,可以操作 UI 组件。
- 不要手动的去调用 AsyncTask 的 onPreExecute, doInBackground, publishProgress, onProgressUpdate, onPostExecute 方法,这些都是由Android 系统自动调用的
- 一个任务 AsyncTask 任务只能被执行一次。
- 运行中可以随时调用 cancel(boolean) 方法取消任务,如果成功调用isCancelled() 会返回 true,并且不会执行 onPostExecute() 方法了,取而代之的是调用 onCancelled() 方法。而且从源码看,如果这个任务已经执行了这个时候调用cancel是不会真正的把task结束,而是继续执行,只不过改变的是执行之后的回调方法是 onPostExecute 还是 onCancelled。
14,接口的意义:
规范,扩展,回调。
15,抽象类的意义:
- 为其他子类提供一个公共的类型;
- 封装子类中重复定义的内容;
- 定义抽象方法,子类虽然有不同的实现,但是定义时一致的;