Glide是一个第三方图片框架,高效,能够内存溢出。Glide不会直接将图片的完整尺寸全部加载到内存中,而是会自动判断ImageView的大小,然后只将ImageView计算出来的大小图片像素加载到内存中,帮助我们节省内存的开支。
with()方法可以接收Context、Activity或者Fragment类型的参数、ApplicationContext。ApplicationContext,传入到with()方法当中。注意with()方法中传入的实例会决定Glide加载图片的生命周期,如果传入的是Activity或者Fragment的实例,那么当这个Activity或Fragment被销毁的时候,图片加载也会停止。如果传入的是ApplicationContext,那么只有当应用程序被杀掉的时候,图片加载才会停止。
线程安全就是当多个线程访问一个对象时,不用考虑调度和交替执行,也不需要同步,不需要任何其他的协调操作, 可以获得正确的结果,那这个对象是线程安全的。保证线程安全可从多线程三特性出发:
原子性(Atomicity):单个或多个操作是要么全部执行,要么都不执行
Lock:保证同时只有一个线程能拿到锁,并执行申请锁和释放锁的代码
synchronized:对线程加独占锁,被它修饰的类/方法/变量只允许一个线程访问
可见性(Visibility):当一个线程修改了共享变量的值,其他线程能够立即得知这个修改
volatile:保证新值能立即同步到主内存,且每次使用前立即从主内存刷新;
synchronized:在释放锁之前会将工作内存新值更新到主存中
有序性(Ordering):程序代码按照指令顺序执行
volatile: 本身就包含了禁止指令重排序的语义
synchronized:保证一个变量在同一个时刻只允许一条线程对其进行lock操作,使得持有同一个锁的两个同步块只能串行地进入
客户端向服务端发送一个表示建立连接的报文段SYN报文段;一旦包含SYN报文段的IP数据报到达服务器主机,服务器从IP数据报中提取出TCP、SYN报文段,为该TCP连接分配需要的缓存和变量,并向客户端发送表示允许连接的报文段ACK;在收到ACK报文段之后,客户端也要给该连接分配缓存和变量,客户端向服务器再发送一个报文段ACK,表示对允许连接的报文段进行了确认。自此完成一次TCP连接。
第三次握手可以避免由于客户端延迟的请求连接的请求,使得服务端无故再次建立连接。
(2)断开TCP连接:TCP的四次挥手
由于TCP连接是全双工的,因此每个方向都必须单独关闭。客户端在数据发送完毕后发送一个结束数据段FIN,且服务端也返回确认数据段ACK,此时结束了客户端到服务端的连接;然后客户端接收到服务端发送的FIN,且服务端也收到了ACK之后,自此双方的数据通信完全结束。简单说来是 “先关读,后关写”,一共需要四个阶段:服务器读通道关闭->客户机写通道关闭->客户机读通道关闭->服务器写通道关闭。
回收算法有以下四种:
standard 模式
这是默认模式,每次激活Activity时都会创建Activity实例,并放入任务栈中。使用场景:大多数Activity。
singleTop 模式
如果在任务的栈顶正好存在该Activity的实例,就重用该实例( 会调用实例的 onNewIntent() ),否则就会创建新的实例并放入栈顶,即使栈中已经存在该Activity的实例,只要不在栈顶,都会创建新的实例。使用场景如新闻类或者阅读类App的内容详情页面。
singleTask 模式
如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的 onNewIntent() )。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移出栈。如果栈中不存在该实例,将会创建新的实例放入栈中。使用场景如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面,或者项目中的主界面。
singleInstance 模式
在一个新栈中创建该Activity的实例,并让多个应用共享该栈中的该Activity实例。一旦该模式的Activity实例已经存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例( 会调用实例的 onNewIntent() )。其效果相当于多个应用共享一个应用,不管谁激活该 Activity 都会进入同一个应用中。使用场景如闹铃提醒,将闹铃提醒与闹铃设置分离。singleInstance不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,在此启动,首先打开的是B
冒泡排序。
特点:相邻两个元素进行比较。
内循环结束一次,最值出现在最后角标位。
*/
public static void bubbleSort(int[] arr)
{
for(int x=0; x<arr.length-1; x++)
{
for(int y=0; y<arr.length-x-1; y++)
{
if(arr[y]>arr[y+1])
{
int temp = arr[y];
arr[y] = arr[y+1];
arr[y+1] = temp;
}
}
}
}
Activity的启动过程,我们可以从Context的startActivity说起,其实现是ContextImpl的startActivity,然后内部会通过Instrumentation来尝试启动Activity,这是一个跨进程过程,它会调用ams的startActivity方法,当ams校验完activity的合法性后,会通过ApplicationThread回调到我们的进程,这也是一次跨进程过程,而applicationThread就是一个binder,回调逻辑是在binder线程池中完成的,所以需要通过Handler H将其切换到ui线程,第一个消息是LAUNCH_ACTIVITY,它对应handleLaunchActivity,在这个方法里完成了Activity的创建和启动,接着,在activity的onResume中,activity的内容将开始渲染到window上,然后开始绘制直到我们看见。
通过startService启动后,service会一直无限期运行下去,只有外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁。
要创建一个这样的Service,你需要让该类继承Service类,然后重写以下方法:
1.如果service没被创建过,调用startService()后会执行onCreate()回调;
2.如果service已处于运行中,调用startService()不会执行onCreate()方法。
如果多次执行了Context的startService()方法,那么Service的onStartCommand()方法也会相应的多次调用。
Service中的onBind()方法是抽象方法,Service类本身就是抽象类,所以onBind()方法是必须重写的,即使我们用不到。
在销毁的时候会执行Service该方法。
这几个方法都是回调方法,且在主线程中执行,由android操作系统在合适的时机调用。
bindService启动服务特点:
1.bindService启动的服务和调用者之间是典型的client-server模式。调用者是client,service则是server端。service只有一个,但绑定到service上面的client可以有一个或很多个。这里所提到的client指的是组件,比如某个Activity。
&Service服务端
要想让Service支持bindService调用方式,需要做以下事情:
1.在Service的onBind()方法中返回IBinder类型的实例。
2.onBInd()方法返回的IBinder的实例需要能够返回Service实例本身。通常,最简单的方法就是在service中创建binder的内部类,加入类似getService()的方法返回Service,这样绑定的client就可以通过getService()方法获得Service实例了。
&client端要做的事情:
1.创建ServiceConnection类型实例,并重写onServiceConnected()方法和onServiceDisconnected()方法。
2.当执行到onServiceConnected回调时,可通过IBinder实例得到Service实例对象,这样可实现client与Service的连接。
3.onServiceDisconnected回调被执行时,表示client与Service断开连接,在此可以写一些断开连接后需要做的处理。
10.1 静态注册方式
一个广播接收器类继续需要重写父类的onReceive()方法
在AndroidManifest.xml文件中注册广播
10.2 动态注册方式
一个广播接收器类 —— 实质就是一个继承自BoradCastReceiver的类,只要继承这个类,就
具体接收广播的能力了,但是能接受什么广播由下面的第三条决定。
重写父类的onReceive()方法 —— 接收到广播的时候,就会回调这个方法。因此,广播接收
器的处理逻辑就写在这里
一个 IntentFilter 对象,广播接收器接收什么样的广播,由它的addAction()方法决定。
在代码中注册广播接收器,通过registerReceiver方法。方法接受两个参数,一个是广播接收
器实例,一个是IntentFilter实例。
取消注册广播 ,通过unregisterReceiver() 方法—— 在哪里取消注册无所谓,只要保证取
消注册就OK
10.3 静态注册方式和动态注册方式的区别
动态注册是在Java类中注册,而静态注册是在AndroidManifest.xml中注册。动态注册的广播接收器不是常驻型的,会随着所注册的Activity的结束而结束,如果所在的Activity已经destroy了,那么该广播接收器也就不能再继续接收广播了。注意:在Activity结束前,要取消注册广播接收器,不然会导致内存泄露;静态注册的广播接收器是常驻型的,即使所在的APP被关闭了,也是可以接收到广播的。动态注册优先级高于静态注册。
ContentProvider(内容提供者)为不同的程序之间数据共享。多用于共享数据库数据给外部的应用。也就是说,如果我们想让其他的应用使用我们自己程序内的数据,就可以使用ContentProvider定义一个对外开放的接口,从而使得其他的应用可以使用咱们应用的文件、数据库内存储的信息。在Android系统中,很多系统自带应用,比如联系人信息,图片库,音频库等应用,都使用了ContentProvider。如果我们想对外提供数据,我们需要继承ContentProvider,并且实现下面的这几个方法:
Handler试用方式
Handler使用方式 因发送消息到消息队列的方式不同而不同,共分为2大类:使用 Handler.sendMessage()、使用Handler.post()
Handler中四个重要的类:
处理器 类(Handler)
消息队列 类(MessageQueue)
循环器 类(Looper)
消息 类(Message)
当创建Handler对象时,则通过 构造方法 自动关联当前线程的Looper对象 & 对应的消息队列对象(MessageQueue),从而 自动绑定了 实现创建Handler对象操作的线程。
消息循环的操作 = 消息出队 + 分发给对应的Handler实例
分发给对应的Handler的过程:根据出队消息的归属者通过dispatchMessage(msg)进行分发,最终回调复写的handleMessage(Message msg),从而实现 消息处理 的操作
特别注意:在进行消息分发时(dispatchMessage(msg)),会进行1次发送方式的判断:
若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息,则直接回调Runnable对象里复写的run()
若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息,则回调复写的handleMessage(msg)
大概原理(了解)
创建主线程时,会自动调用ActivityThread的1个静态的main();而main()内则会调用Looper.prepareMainLooper()为主线程生成1个Looper对象,同时也会生成其对应的MessageQueue对象
即 主线程的Looper对象自动生成,不需手动生成;而子线程的Looper对象则需手动通过Looper.prepare()创建
在子线程若不手动创建Looper对象 则无法生成Handler对象
根据Handler的作用(在主线程更新UI),故Handler实例的创建场景 主要在主线程
生成Looper & MessageQueue对象后,则会自动进入消息循环:Looper.loop(),即又是另外一个隐式操作
(1)、当我们通过OkhttpClient创建一个Call,并发起同步或异步请求时;
(2)、okhttp会通过Dispatcher对我们所有的RealCall(Call的具体实现类)进行统一管理,并通过execute()及enqueue()方法对同步或异步请求进行处理;
(3)、execute()及enqueue()这两个方法会最终调用RealCall中的getResponseWithInterceptorChain()方法,从拦截器链中获取返回结果;
(4)、拦截器链中,依次通过RetryAndFollowUpInterceptor(重定向拦截器)、BridgeInterceptor(桥接拦截器)、CacheInterceptor(缓存拦截器)、ConnectInterceptor(连接拦截器)、CallServerInterceptor(网络拦截器)对请求依次处理,与服务的建立连接后,获取返回数据,再经过上述拦截器依次处理后,最后将结果返回给调用方。
View是所有UI组件的基类,而 ViewGroup是容纳这些组件的容器,其本身也是从View派生出来的.简单的说就是:view指某些具体的控件,如Textview,imageview等,ViewGroup是用来盛放这些控件的容器,如LinearLayout和Relativelayout等
这里主要说一下绘制的区别:
UI绘制主要有五个方法:onDraw(),onLayout(),onMeasure(),dispatchDraw(),drawChild()
1.ViewGroup包含这五个方法,而View只包含onDraw(),onLayout(),onMeasure()三个方法,不包含dispatchDraw(),drawChild()。
2.绘制流程:onMeasure(测量)——》onLayout(布局)——》onDraw(绘制)。
3.绘制按照视图树的顺序执行,视图绘制时会先绘制子控件。如果视图的背景可见,视图会在调用onDraw()之前调用drawBackGround()绘制背景。强制重绘,可以使用invalidate();
4.如果发生视图的尺寸变化,则该视图会调用requestLayou(),向父控件请求再次布局。如果发生视图的外观变化,则该视图会调用invalidate(),强制重绘。如果requestLayout()或invalidate()有一个被调用,框架会对视图树进行相关的测量、布局和绘制。
注意:视图树是单线程操作,直接调用其它视图的方法必须要在UI线程里。跨线程的操作必须使用Handler。
5.onLayout():对于View来说,onLayout()只是一个空实现;而对于ViewGroup来说,onLayout()使用了关键字abstract的修饰,要求其子类必须重载该方法,目的就是安排其children在父视图的具体位置。
6.draw过程:drawBackground()绘制背景——》onDraw()对View的内容进行绘制——》dispatchDraw()对当前View的所有子View进行绘制——》onDrawScrollBars()对View的滚动条进行绘制
JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。
栈:在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它
堆:堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。
ArrayList和LinkedList两者都实现了List接口
(1)ArrayList是底层是数组,所以它提供对元素的随机访问,复杂度为O(1),但LinkedList底层是链表,每个节点都有一个引用来关联下一个节点。所以,尽管有使用索引获取元素的方法,内部实现是从起始点开始遍历,遍历到索引的节点然后返回元素,时间复杂度为O(n),比ArrayList要慢。
(2)与ArrayList相比,在LinkedList中插入、添加和删除一个元素会更快,因为在一个元素被插入到中间的时候,不会涉及改变数组的大小,或更新索引。
(3)LinkedList比ArrayList消耗更多的内存,因为LinkedList中的每个节点存储了前后节点的引用。
强引用: 通常我们使用new操作符创建一个对象时所返回的引用即为强引用;
软引用: 若一个对象只能通过软引用到达,那么这个对象在内存不足时会被回收,可用于图片缓存中,内存不足时系统会自动回收不再使用的Bitmap;
弱引用: 若一个对象只能通过弱引用到达,那么它就会被回收(即使内存充足),同样可用于图片缓存中,这时候只要Bitmap不再使用就会被回收;
虚引用: 虚引用是Java中最“弱”的引用,通过它甚至无法获取被引用的对象,它存在的唯一作用就是当它指向的对象回收时,本身会被加入到引用队列中,这样我们可以知道它指向的对象何时被销毁。
String:不可变字符串;频繁操作时,每次都需新开辟内存,极易造成内存浪费
StringBuffer:可变字符串、效率低、线程安全;执行速度慢
StringBuilder:可变字符串、效率高、线程不安全;执行速度快
(1)如果要操作少量的数据用 String;
(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;
(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder(推荐使用)。
StringBuffer和StringBuilder类的对象能够被多次的修改,并且不产生新的未使用对象。
StringBuilder类在Java5中被提出,它和StringBuffer之间的最大不同在于StringBuilder的方法不是线程安全的(不能同步访问)。
由于StringBuilder相较于StringBuffer有速度优势,所以多数情况下建议使用StringBuilder类。
然而在应用程序要求线程安全的情况下,则必须使用StringBuffer类。
一、onAttach()
作用:fragment已经关联到activity。
二、onCreate()
作用:系统创建fragment的时候回调他,在他里面实例化一些变量,这些个变量主要是:当你暂停停止的时候你想保持的数据
,只调用一次。
三、onCreateView()
作用: 第一次使用的时候 fragment会在这上面画一个layout出来, 为了可以画控件 要返回一个 布局的view,当系统用到fragment的时候 fragment就要快速返回它的view,所以尽量在这里不要做耗时操作,比如从数据库加载大量数据
四、onActivityCreated()
当Activity中的onCreate方法执行完后调用。
五、onStart()
启动Fragement 启动时回调,,此时Fragement可见。
六、onResume()
在activity中的运行是可见的。激活, Fragement 进入前台, 可获取焦点时激活。
七、onPause()
其他的activity获得焦点,这个仍然可见第一次调用的时候,指的是用户离开这个fragment(并不是被销毁)
通常用于用户的提交(可能离开页面后不会再回到这个页面)
八、onStop()
fragment是不可见的,可能出现的情况:activity被stopped了或者 fragment被移除但被,加入到回退栈中,一个stopped的fragment仍然是活着的如果长时间不用也会被移除。
九、onDestroyView()
Fragment中的布局被移除时调用。表示fragemnt销毁相关联的UI布局, 清除所有跟视图相关的资源。然后这个只是移除视图 并没有销毁而且也没有脱离activity
十、onDestroy()
销毁fragment对象。
十一、onDetach()
Fragment和Activity解除关联的时候调用。 脱离activity
onCreate:表示Activity正在被创建,这是生命周期的第一个方法。在这个方法中可以做一些初始化的工作(加载布局资源、初始化Activity所需要的数据等),耗时的工作在异步线程上完成。
onRestart:表示Activity正在重新启动。一般情况下,在当前Activity从不可见重新变为可见的状态时onRestart就会被调用。这种情形一般是由于用户的行为所导致的,比如用户按下Home键切换到桌面或者打开了一个新的Activity(这时当前Activity会暂停,也就是onPause和onStop被执行),接着用户有回到了这个Activity,就会出现这种情况。
onStart: Activity正在被启动,并且即将开始。但是这个时候要注意它与onResume的区别。两者都表示Activity可见,但是onStart时Activity还正在加载其他内容,正在向我们展示,用户还无法看到,即无法交互。
onResume: 表示Activity已经创建完成,并且可以开始活动了,这个时候用户已经可以看到界面了,并且即将与用户交互(完成该周期之后便可以响应用户的交互事件了)。
onPause: 表示Activity正在暂停,正常情况下,onStop接着就会被调用。在特殊情况下,如果这个时候用户快速地再回到当前的Activity,那么onResume会被调用(极端情况)。一般来说,在这个生命周期状态下,可以做一些存储数据、停止动画的工作,但是不能太耗时,如果是由于启动新的Activity而唤醒的该状态,那会影响到新Activity的显示,原因是onPause必须执行完,新的Activity的onResume才会执行。
onStop:表示Activity即将停止,可以做一些稍微重量级的回收工作,同样也不能太耗时(可以比onPause稍微好一点)。
onDestroy:表示Activity即将被销毁,这是Activity生命周期的最后一个回调,我们可以做一些回收工作和最终的资源释放(如Service、BroadReceiver、Map等)。
EventBus的优势 简化了组件之间的通信
1、事件发送者和接收者的解耦
2、很好地工作在Activities, Fragments,后台线程中
3、避免复杂且容易出错的依赖关系和生命周期问题
一、Eventbus的创建
Eventbus的构造方法是由public修饰的,this(DEFAULT_BUILDER)对eventbus进行初始化。在构造方法中创建了3个关键Post它们负责线程间的调度,分别是mainThreadPoster、backgroundPoster、asyncPoster 。但是最终还是通过构建者模式Builder内部类的形式创建的。
二、subscribe注解
修饰 接受事件的方法,也可以添加threadmode 确定线程的模式。
也可以添加sticky属性判断是否为粘性事件 :事件消费者在事件发布之后才注册的也能接收到该事件的特殊类型。
@Subscribe(threadMode = ThreadMode.MAIN)
public void XXX(MessageEvent messageEvent) {
...
}
什么叫序列化:
为什么要序列化?
java对象序列化后可以很方便的存储或者在网络中传输
具体过程:(了解)
序列化操作时,系统会把当前类的serialVersionUID写到序列化的文件当中,而当反序列化时系统又会检测文件当中的serialVersionUID,判断是否和当前类的serialVersionUID一致,如果一致就说明序列化的版本和当前类的序列化版本是一样的,那么就可以反序列化反之就反序列化失败。
Serializable和Parcelable 区别
Serializable是java提供的一个序列化接口,是一个空接口,专门为对象提供标准序列化和反序列化操作,实现类的序列化比较简单,只要在类的声明中实现Serializable接口即可。
两者的设计初衷:
(1)Serializable的设计初衷是为了序列化对象到本地文件、数据库、网络流、RMI以便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。Serializable效率过低,消耗大
(2)Parcelable主要用在内存环境中(内存属于android中的稀有资源),因此Parcelable的出现是为了满足数据在内存中低开销而且高效地传递问题。
两者的区别:
(1)Serializable实现只需要通过一个Serializable接口就可以实现给这个对象添加一个serialVersionUID,系统会自动将其序列化
(2)Parcelable实现不仅需要实现Parcelable接口还需要在类中添加一个静态成员变量CREATOR,这个变量需要实现Parceable.Creator接口,并实现读写的抽象方法。
(3)Serializable使用IO读写存储在硬盘上。在持久化操作方面较方便,所以序列化到存储设备中一般使用Serializable。
(4) Parcelable直接在内存中读写,性能好特别是在内存开销方面,所以Android应用在内存间传输数据一般用Parcelable.如activity间传输数据和AIDL.
一种是Android去调用js的方法,
一种是js调用Android的方法。
而相互调用的桥梁就是我们的WebView了。首先我们需要先设置webview支持js:webSettings.setJavaScriptEnabled(true);
1.Bundle 只能传输Bundle支持的数据类型,适用于四大组件间的进程间通信。
文件共享 不适用高并发场景,并且无法做到进程间即时通信,适用于无关发的情况下,交换简单的数据,对实
时性要求不高的场景。
2.AIDL 功能强大,支持一对多实时并发通信,使用稍复杂,需要处理好线程间的关系,一对多通信且有RPC需求
3.Messenger 功能一般,支持一对多串行通信,支持实时通信,不能很好地处理高并发的情形,不支持RPC,
由于数据通过Message传输,因此只能传输Bundle支持的数据类型,低并发的一对多实时通信,无RPC需求,
或者无需要返回结果的RPC需求
4.ContentProvider 支持一对多的实时并发通信,在数据源共享方面功能强大,可通过Call方法扩展其它操
作,可以理解为受约束的AIDL,主要提供对数据源的CRUD操作.
5.BroadcastReceiver 操作简单,对持一对多实时通信,只支持数据单向传递,效率低且安全性不高,
一对多的低频率单向通信
6.Socket 功能强大,可通过网络传输字节流,支持一对多实时并发通信,实现细节步骤稍繁琐,不支持直接的
RPC,网络间的数据交换
由于不同的进程拥有不同的数据空间,所以无论是应用内还是应用间,均无法通过共享内存来实现进程间通信。
应用内使用多进程可能导致哪些问题?
当一个APP启用了多进程后,系统会为不同的进程分配不同的内存空间,因此所有需要通过内存共享的行为都会失败。另外,还会导致以下几个问题:
进程:Application会被多次创建,因为系统会为每一个进程分配独立的内存空间。
线程:线程同步失效,因为不同进程中的线程同步锁不是同一个对象。
内存:单例模式和静态变量失效,因为不同进程间的内存空间不同。
存储:SharedPreferences可靠性下降,因为系统对SharedPreferences有一定的缓存策略,多个进程同时读写可能会造成数据丢失。
抽象:对现实世界的事物进行概括,抽象为在计算机虚拟世界中有意义的实体
封装:将某事物的属性和行为包装到对象中,构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,并且尽可
能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系
继承:子类继承父类,不仅可以有父类原有的方法和属性,也可以增加自己的或者重写父类的方法及属性
多态:允许不同类的对象对同一消息做出各自的响应
(1) Throwable继承层次结构,可见分成两大类Error和Exception:
Error(错误):指程序无法恢复的异常情况,表示运行应用程序中较严重的问题;发生于虚拟机自身、或者在虚拟机试图执
行应用时,如Virtual MachineError(Java虚拟机运行错误)、NoClassDefFoundError(类定义错误);属于不可
查异常,即不强制程序员必须处理,即使不处理也不会出现语法错误。
Exception(异常):指程序有可能恢复的异常情况,表示程序本身可以处理的异常。又分两大类:
RuntimeException(运行时异常):由程序自身的问题导致产生的异常;如NullPointerException(空指针异常)、
IndexOutOfBoundsException(下标越界异常);属于不可查异常。
非运行时异常:由程序外部的问题引起的异常;除了RuntimeException以外的异常,如FileNotFoundException(文
件不存在异常);属于可查异常,即强制程序员必须进行处理,如果不进行处理则会出现语法错误。
(2)常见的异常处理机制有:
捕捉异常:由系统自动抛出异常,即try捕获异常->catch处理异常->finally 最终处理
抛出异常:在方法中将异常对象显性地抛出,之后异常会沿着调用层次向上抛出,交由调用它的方法来处理。配合throws声
明抛出的异常和throw抛出异常
自定义异常:继承Execption类或其子类
强引用(StrongReference):具有强引用的对象不会被GC;即便内存空间不足,JVM宁愿抛出OutOfMemoryError使程
序异常终止,也不会随意回收具有强引用的对象。
软引用(SoftReference):只具有软引用的对象,会在内存空间不足的时候被GC;软引用常用来实现内存敏感的高速缓
存。
弱引用(WeakReference):只被弱引用关联的对象,无论当前内存是否足够都会被GC;强度比软引用更弱,常用于描述
非必需对象;常用于解决内存泄漏的问题
虚引用(PhantomReference):仅持有虚引用的对象,在任何时候都可能被GC;常用于跟踪对象被GC回收的活动;必须
和引用队列 (ReferenceQueue)联合使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对
象的内存之前,把这个虚引用加入到与之关联的引用队列中。
RetryAndFollowUpInterceptor:负责请求的重试和重定向
BridgeInterceptor:给请求添加对应的 header 信息,处理响应结果的 header 信息
CacheInterceptor:根据当前获取的状态选择 网络请求 、读取缓存、更新缓存。
ConnectInterceptor:建立 http 连接。
CallServerInterceptor:读写网络数据。
在创建Fragment的时候调用这个Fragment的setArguments()传入一个Bundle对象传入数据(不推荐传非基本类型的)
或在Fragment的构造中添加需要参数的构造
还可以在Fragment中直接创建一个方法 在创建Fragment时调用方法传入数据
(如果传入的数据需要逻辑处理最好不要用,这个方法在调用时Fragment的view还没创建完成,只能进行传值)
在Fragment调用getActivity()返回的activity就是存有在这个Fragment的Activity对象,可以强转为这个Activity的对象然后调用方法直接传值
或在Fragment中创建一个接口 然后由创建这个Fragment的Activity实现这个接口就可以让Fragment传值到Activity了
用getActivity()获取到当前依附的Activity然后强转后调用Activity的方法获取到想要的Fragment然后传值,setArguments()或直接调用方法
或者以FragmentManeage调用findFragmentById("ID"),或者ByTag()也可以前提是这个Fragment有Tag,
或者用FragmentManeage获取到Fragment的集合getFragments(),然后手动遍历判断集合的Fragment是否是需要的Fragmeng然后setArguments()或直接调用方法传值(不推荐)
可以直接使用广播传值 或者 使用EventBus传值
Android系统中,ActivityManagerService(简称AMS)和WindowManagerService(简称WMS)会检测App的响应时间,如果App在特定时间无法相应屏幕触摸或键盘输入时间,或者特定事件没有处理完毕,就会出现ANR。
以下四个条件都可以造成ANR发生:
• InputDispatching Timeout:5秒内无法响应屏幕触摸事件或键盘输入事件
• BroadcastQueue Timeout :在执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒。
• Service Timeout :前台服务20秒内,后台服务在200秒内没有执行完毕。
• ContentProvider Timeout :ContentProvider的publish在10s内没进行完。
基本都是主线程被阻塞,所以说尽量避免在主线程(UI线程)中作耗时操作。
抽象:对现实世界的事物进行概括,抽象为在计算机虚拟世界中有意义的实体
封装:将某事物的属性和行为包装到对象中,构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,并且尽可
能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系
继承:子类继承父类,不仅可以有父类原有的方法和属性,也可以增加自己的或者重写父类的方法及属性
多态:允许不同类的对象对同一消息做出各自的响应
String是字符串常量,而StringBuffer、StringBuilder都是字符串变量,即String对象一创建后不可更改,而后两
者的对象是可更改的:
StringBuffer是线程安全的,而StringBuilder是非线程安全的,这是由于StringBuffer对方法加了同步锁或者对调
用的方法加了同步锁
String更适用于少量的字符串操作的情况,StringBuilder适用于单线程下在字符缓冲区进行大量操作的情况,
StringBuffer适用于多线程下在字符缓冲区进行大量操作的情况
:
通过String a="“直接赋值的方式得到的是一个字符串常量,存在于常量池;注意,相同内容的字符串在常量池中只有一
个,即如果池已包含内容相等的字符串会返回池中的字符串,反之会将该字符串放入池中
通过new String(”")创建的字符串不是常量是实例对象,会在堆内存开辟空间并存放数据,且每个实例对象都有自己的地
址空间
引申:对于用String a="“和String a=new String(”")两种方式定义的字符串,判断使用equals()、"=="比较结果
是什么
equals:如果是object 类型对比的内存地址值,如果是字符串,比较的是两个字符串的内容是否相同
" == " :比较的是两个对象的内存地址是 否相同
参考回答:装箱就是自动将基本数据类型转换为包装器类型,拆箱就是自动将包装器类型转换为基本数据类型
Integer是int的包装类,int则是java的一种基本数据类型
Integer变量必须实例化后才能使用,而int变量不需要
Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
Integer的默认值是null,int的默认值是0
含义:在运行状态中,对于任意一个类都能知道它的所有属性和方法,对于任何一个对象都能够调用它的任何一个方法和属
性。
功能:动态性,体现在:在运行时判断任意一个类所具有的属性和方法; 在运行时判断任意一个对象所属的类;在运行时构
造任意一个类的对象;在运行时调用任意一个对象的方法;生成动态代理
应用:反射&泛型
参考回答:内部类就是定义在另外一个类里面的类。它隐藏在外部类中,封装性更强,不允许除外部类外的其他类访问它;
但它可直接访问外部类的成员。静态内部类和非静态内部类的区别有:
静态内部类是指被声明为static的内部类,可不依赖外部类实例化;而非静态内部类需要通过生成外部类来间接生成。
静态内部类只能访问外部类的静态成员变量和静态方法,而非静态内部类由于持有对外部类的引用,可以访问外部类的所用
成员
final关键字表示不可更改,具体体现在:
final修饰的变量必须要初始化,且赋初值后不能再重新赋值
final修饰的方法不能被子类重写
final修饰的类不能被继承
finally:和try、catch成套使用进行异常处理,无论是否捕获或处理异常,finally块里的语句都会被执行,在以下4种
特殊情况下,finally块才不会被执行:
在finally语句块中发生了异常
在前面的代码中用了System.exit()退出程序
程序所在的线程死亡
关闭CPU
finalize():是Object中的方法,当垃圾回收器将回收对象从内存中清除出去之前会调用finalize(),但此时并不代表 该回收对象一定会回收。
重写表示子类重写父类的方法;重载表示有多个同名函数同时存在,区别在于有不同的参数个数或类型
引申:谈谈动态分派和静态分派
使用上的区别:一个类只能继承一个抽象类却可以实现多个接口
设计上的区别:接口是对行为的抽象,无需有子类的前提,是自上而下的设计理念;抽象类是对类的抽象,建立于相似子类
之上,是自下而上的设计理念
一方面,由于方法中的局部变量的生命周期很短,一旦方法结束变量就要被销毁,为了保证在内部类中能找到外
部局部变量,通过final关键字可得到一个外部变量的引用;另一方面,通过final关键字也不会在内部类去做修改该变量
的值,保护了数据的一致性。
equals(): 和==作用相似
hashCode():用于哈希查找,重写了equals()一般都要重写该方法
getClass(): 获取Class对象
wait():让当前线程进入等待状态,并释放它所持有的锁
notify()¬ifyAll(): 唤醒一个(所有)正处于等待状态的线程
toString():转换成字符串
可将Java集合框架大致可分为Set、List、Queue 和Map四种体系
Set:代表无序、不可重复的集合,常见的类如HashSet、TreeSet
List:代表有序、可重复的集合,常见的类如动态数组ArrayList、双向链表LinkedList、可变数组Vector
Map:代表具有映射关系的集合,常见的类如HashMap、LinkedHashMap、TreeMap
Queue:代表一种队列集合