HashMap是线程不安全的:意思是在多个线程中使用HashMap时,例如线程A(客户)和线程B(客户)去共享资源(窗口)买票,还剩1张票时,线程不安全的话,他们两个人就会同时买到这一张票。
HashTable是线程安全的:意思在多个线程中使用HashTable时,买票为例子:如果A先买票,那么就是A有票,B就无法买票了。主要看谁网速快,谁先买到。使线程安全的原因是在方法中加了synchronized这个关键字,使得线程必须同步执行,即线程A执行完了过后,线程B才能执行。
ArrayList:底层基于数组形式,数组的特点,是占用连续的内存空间 且内部实现了快速随机访问,宏观上所以查找很快,插入和删除数据很慢。为什么会出现这种情况?答:因为数组查询数据快是数组的特点,内存是连续的,插入和删除慢是因为,在数组中间插入一个值,那么后面的所有数组数据就要往后移,开销比较大,速度就比较慢,删除也是同理。
LinkedList:底层基于链表形式,链表形式即(一个节点,包含了节点数据和指向下一个节点的指针),宏观上插入和删除比较快,查询比较慢。为什么会出现这种情况?答:插入和删除快,因为链表的特点,删除某个数据节点,去删除节点的时候会把链表分为两半,判断删除的节点在前半段还是后半段,如果是在前半段,则从前半段的末尾节点去从后往前遍历找到要删除的节点,如果是后半段,则从后半段的链表头部,从前往后遍历查找要删除的节点。而不需要去全部遍历,即找到前一个指向删除节点的指针域就可以了。而查询慢是因为链表要程序找指针域,一个个去遍历下来,开销大。
特殊情况:
ArrayList在数组后面插入一个数,则不需要改动数组的位置,因为在后面插入数,前面的数不需要变化,这样的情况跟linkedList得插入差不多。
LinkedList去查询一个数的话,如果那个数(节点)正好在中间,则跟ArrayList查询差不多快,因为LinkedList分为了前半段和后半段去查询数(节点)。
ArrayList扩容机制:当容量满了的时候会重新创建一个比之前的要大1.5倍的ArrayList,再把之前的ArrayList重新按顺序放进1.5倍打的ArrayList中。
HashMap底层是以数组+链表形式去存储数据的,即每个数组元素都是一个链表,称为 桶。HashMap存储键值对,实现快速存取数据;允许null键/值
put()操作:首先会计算key的hashcode值,通过这个值和数组的长度进行模运算,得到数组中的元素(桶)就是put要存储的位置,如果key相同,则替换掉之前的value值。
get()操作:首先会计算key的hashcode值,通过这个值和数组的长度进行模运算,得到数组中的元素(桶),再对它的key进行做hashcode计算,如果hashcode值相同(产生碰撞),则比对它们的equals是否相同,如果相同则找到了要得到的位置取出即可。如果没发生碰撞,则不需要进行equals。
HashMap扩容机制:,阈值=loadfactor*current capacity,load factor默认0.75。HashMap内部的参数就是loadfactor和current capacity,第一个参数默认为0.75,(75%),第二个参数为HashMap的大小。如果HashMap的容量占用到了75%时,会重新扩容从两倍容量。
onCreate()
当活动创建时执行
onStart()
当活动可见时执行,就是进入前台时
onResme()
这个方法在活动准备好和用户进行交互的时候,并且此时的活动一定位于返回栈的栈顶,并且处于运行状态时执行。
onPause()
当活动部分可见时执行,声明:dialog提示窗并不会执行此方法,网上那些都是愣头青,瞎转载没测试的。为什么说当活动部分可见时执行呢,如果是activity设置为dialog形式的时候才会调用。
添加 android:theme="@android:style/Theme.Dialog" 。将Activity修改为Dialog样式。 即可
当 当前活动全部不可见时也会调用。
onStop()
当活动不可见时调用
onDestory()
当活动销毁时调用,比如按了手机自带返回键,或者设置了Finnish()
补充Intent【A跳转到B】的生命周期
A:代表A的生命周期;
B:代表B的生命周期;
onCreate(A)→onStart(A)→onResme(A)→onPause(A)→onCreate(B)→onStart(B)→onResme(B)→onStop(A)
当又返回A时
onPause(B)→onRestart(A)→onStart(A)→onResme(A)→onStop(B)→onDestroy(B)
Dialog和AlertDialog并不会影响到Activity的生命周期
因为dialog依附于 activity, 并没有被遮挡。
Intent传递的数据类型必须是八种基本数据类型(int ,float …)和必须实现序列化的类。
序列化(Servalable,Parceable)两种序列化。
序列化是什么:我们的对象并不只是存在内存中,还需要传输网络,或者保存起来下次再加载出来用,所以需要Java序列化技术。Java序列化技术正是将对象转变成一串由二进制字节组成的数组,可以通过将二进制数据保存到磁盘或者传输网络,磁盘或者网络接收者可以在对象的属类的模板上来反序列化类的对象,达到对象持久化的目的。
两种序列化的差别:第一种是java自带的序列化,用于把数据序列化从文件的形式保存到磁盘,但是会产生大量的临时变量,导致很卡(GC(垃圾回收)发生频繁),不适用内存中的传输数据。
第二种是Android自带的序列化,不能把数据序列化到磁盘上,序列化从二进制形式在内存上进行数据传输。
1.补间动画/视图动画
没有改变实际的view宽高大小,只是改变了视图显示位置。那么在动画移动过程中,点击事件就无效了。因为实际的view没有跟着动
2.帧动画
一帧帧的动画,记得图片不要太大,会很容易导致OOM,内存溢出。
3.属性动画
改变了view的实际位置,可以实现点击事件。
动画的一些属性
名称 原理 对应的Animation的子类
平移动画(Translate) 移动视图的位置 TranslateAnimation类
缩放动画(Scale) 放大/缩小 视图的大小 ScaleAnimation类
旋转动画(Rotate) 旋转视图的角度 RotateAnimation类
透明度动画(Alpha) 改变视图的透明度 AlphaAnimation类
1.standard(默认模式)
standard模式下,会不断地新建activity实例,都放入同一个堆栈中
2.singleTop
如果当前实例在当前的task的栈顶,则直接复用,如果当前实例不再栈顶,则新建实例。
3.singleTask
设置了singleTask的页面,只要是task中有这个实例,就会一直复用,而且每次复用这个已存在的实例,都会清空上面的其他实例,将自己直接提升到栈顶位置,显示自己
4.singleInstance
设置了singleInstance的页面,初始时都是新建一个task栈给这个页面,然后后面一直复用这个栈内的页面。注意,只有这种模式会新建一个栈给初始化的页面。最后返回的规律是,先把本页面所在的栈都出完,然后再弹出下一个栈的每个页面。
大概的说一下。
分为4个角色:
handler:跨线程数据接受者。
message:跨线程传输消息的消息主体。
message enqueue:消息队列,以链表的形式存储着message。message分为延迟消息和非延迟消息。延迟越大,就放在消息队列的越后面。
Loopr:消息队列(message enqueue)的管理者,控制着消息队列中的消息的存入和放出。线程通过Looper建立自己的消息循环,Looper负责从MessageQueue中取出消息,并且分发到消息指定目标Handler对象。
大概流程
子线程发送数据给主线程时,在子线程中调用,handler.sendMessage(message)(延迟默认为0ms) 发送消息时,会一步步调用走到
handler.sendMessageDelayd(message)中,里面实现了当前时间+消息的延迟时间,接着调用handler.sendMessageAttime(message),确定了消息的延迟发送时间,接着调用message enqueue,准备把message加入消息队列,这时管理权交给了looper中,会得到当前线程,把当前线程设置为安全线程(ThreadLocal)因为可以在很多子线程中发送消息给主线程,每个线程要隔离,防止数据混乱,然后把消息加入了消息队列之中,再调用了target.dispatchMessage()方法,target就是handler,把消息分发给handler,就是把消息回调给handler,就完成了一次handler跨线程操作。
M(model):差不多就是逻辑处理的地方
V(view):就是Actvivity负责从M中放入和取出数据进行展示
P (presenter):中间人。M和V不能直接互动,要通过P才行。V→P↔M,V把数据给P,P再给M,把数据处理后M交给P,P在交给V。
这样设计的好处:低耦合高内聚,不需要改一处而到处都要改。
应用程序无响应。
1.内存溢出
系统已经不能再分配出你所需要的空间,比如系统现在只有1G的空间,但是你偏偏要2个G空间,这就叫内存溢出 。就比如每次都加载很多大图片,然后又不释放图片资源,久而久之,图片常驻内存,内存消耗完了就闪退了。
所以图片要及时释放清除.bitmap就是一种存放图片的格式。bitmap.recycler()进行释放。
2.内存泄漏
强引用所指向的对象不会被回收,可能导致内存泄漏,java(jvm)虚拟机宁愿抛出OOM也不会去回收他指向的对象 意思就是你用资源的时候为他开辟了一段空间,当你用完时忘记释放资源了,这时内存还被占用着,一次没关系,但是内存泄漏次数多了就会导致内存溢出 。jvm的作用之一就是清除垃圾,比如一些没有的实例,new出来的对象之类,让内存更友好,也是java一种特有的机制。
handler也会导致内存泄漏。因为handler要处理线程的事情,如果线程没处理完毕,activity主动finished掉了,那么handler就会一直处理线程的事情。解决办法:就是把handler改为静态内部类 static myHandler class extend handler{} 因为静态内部类不会持有外部类的引用。是一个独立的“类”。
也可以用弱引用软引用的方式去解决这个问题。
Java的内存分配和内存回收,都不需要程序员负责,都是由伟大的JVM去负责,一个对象是否可以被回收,主要看是否有引用指向此对象,说的专业点,叫可达性分析。
Java设计这四种引用的主要目的有两个:
JVM的知识就不讲了。
1.强引用
强引用是最普遍的一种引用,我们写的代码,99.9999%都是强引用:
Object
new
Object
这种就是强引用了,是不是在代码中随处可见,最亲切。
2.软引用
SoftReferencestudentSoftReference=new SoftReference(new Student());
//软引用就是把对象用SoftReference包裹一下,当我们需要从软引用对象获得包裹的对象,只要get一下就可以了:
Student student = studentSoftReference.get();
System.out.println(student);
当内存不足,会触发JVM的GC(垃圾回收),如果GC(垃圾回收)后,内存还是不足,就会把软引用的包裹的对象给干掉,也就是只有在内存不足,JVM才会回收该对象。
什么是垃圾回收:
垃圾回收的垃圾就是 无用类,无用的对象等,把无用的类和实例进行清理掉,节约程序运行时的内存。
3.弱引用
跟上面代码类似。用 WeakReference 对象来包裹
弱引用的特点是不管内存是否足够,只要发生GC,都会被回收: