Android面试笔记

来源:互联网 发布:淘宝客户经理有用吗 编辑:程序博客网 时间:2024/04/27 19:58

主要是我个人为了准备Android面试做的一些笔记记录,来自于互联网他人的整理,我自己摘取下来作为复习使用。

检测Android中内存泄漏:
LeakCanary工具,是一个开源的在debug版本中检测内存泄漏的java库,它能更早的发现内存泄露。
使用:
在项目的build.gradle文件添加:

 dependencies {   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3' }

在Application类添加:

public class ExampleApplication extends Application {  public static RefWatcher getRefWatcher(Context context) {    ExampleApplication application = (ExampleApplication) context.getApplicationContext();    return application.refWatcher;  }  private RefWatcher refWatcher;  @Override public void onCreate() {    super.onCreate();    refWatcher = LeakCanary.install(this);  }}

你可以使用RefWatcher观察Fragment的内存泄露

public abstract class BaseFragment extends Fragment {  @Override public void onDestroy() {    super.onDestroy();    RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());    refWatcher.watch(this);  }}

当在你的debug构建过程中出现内存泄露时,LeakCanary将会自动展示一个通知栏。

Java和Android内存泄露总结:

Java内存分配策略:
1.静态存储区:主要存放静态数据、全局static数据和常量。这块内存在程序编译时就已经分配好,并且在
程序整个运行期间都存在。

2.栈区:当方法被执行时,方法体内的局部变量(其中包括基础数据类型、对象的引用)都在栈上创建,并
在方法执行结束时这些局部变量所持有的内存将会自动被释放。效率很高,但是分配的内存容量有限。

3.堆区:又称动态内存分配,是指在程序运行时直接new出来的内存,也就是对象的实例。这部分内存不使
用时将会有java垃圾回收器来负责回收。

Java是如何管理内存:
1.在Java中,内存的分配是由程序完成的,而内存的释放是由GC完成的。这方法简化了程序员的工作,但是
也加重了JVM的工作,这也是导致Java速度较慢的原因之一,因为GC必须监控每一个对象的运行状态,包括
对象的申请、引用、赋值等。

2.释放对象的根本原则是该对象不再被引用。

Java中的内存泄露:
内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存
空间的浪费,具体表现为:
1.在有向图中,对象可达,存在通路与其相连

2.这些可达的对象是无用的,程序以后不会再使用这些对象

满足以上两个条件,这些对象就不会被GC回收,然而它却占用内存。

Java内存回收机制:
在Java中判断一个内存空间是否符合垃圾收集标准有两个:
1.是给对象赋予了空值null,以下再没有调用过。

2.是给对象赋予了新值,这样重新分配了内存空间。

Java内存泄露原因:
根本原因是长生命周期的对象持有短生命周期对象的引用,这样尽管短生命周期对象虽然不再需要,但是因
为被长生命周期持有它的引用而导致不能被回收,具体表现为以下几大类:
1.静态集合类引起内存泄露。(集合里面存放的是对象,把对象设为null释放是不够的,还要把集合设为
null进行释放)

2.当集合里面的对象属性被修改后,再调用remove()方法时不起作用。(集合里面对象的成员变量值更改以
后,hascode也会改变,这时remove不掉,造成内存泄露)

3.监听器。(增加了以后记得要删除)

4.各种连接。(数据库连接、网络连接、io连接等,除非显示的调用了close()将其连接关闭,否则是不会
自动被GC回收的)

5.内部类和外部模块的引用。

6.单例模式。(单例对象在初始化后将在JVM的整个生命周期中存在,如果单例对象持有外部的引用,那么
这个对象将不能被JVM正常回收,导致内存泄露)

Android中常见的内存泄露汇总:
1.集合类泄露。(集合类如果仅仅有添加元素的方法,而没有删除机制,导致内存被占用,如果这个集合类
也是全局性的静态属性,很可能导致集合所占用的内存只增不减)

2.单例造成的内存泄漏。(由于单例的静态属性使得其生命周期跟应用程序的一样长,如果使用不当,就容
易造成内存泄露)

3.匿名内部类/非静态内部类和异步线程。
a.非静态内部类创建静态实例造成的内存泄露。
b.匿名内部类。(用了匿名类,并被异步线程持有了,如果没有任何措施这样一定会导致泄露)

4.Handler造成的内存泄漏。(很多时候我们为了避免ANR而不在主线程进行耗时操作,在处理网络任务或者封装一些请求回调等api都借助Handler来处理。我们知道 Handler、Message 和 MessageQueue 都是相互关联在一起的,万一 Handler 发送的 Message 尚未被处理,则该 Message 及发送它的 Handler 对象将被线程 MessageQueue 一直持有,由于 Handler 属于 TLS(Thread Local Storage) 变量, 生命周期和 Activity 是不一致的。因此这种实现方式一般很难保证跟 View 或者 Activity 的生命周期保持一致,故
很容易导致无法正确释放)

修复方法:在Activity中避免使用费静态内部类,这样的话对象的生命周期和Activity无关了。同时通过弱
引用的方式引入 Activity,避免直接将 Activity 作为 context 传进去。
综述,即推荐使用静态内部类 + WeakReference 这种方式。每次使用前注意判空。

创建一个静态Handler内部类,然后对 Handler 持有的对象使用弱引用,这样在回收时也可以回收 Handler
持有的对象,但是这样做虽然避免了 Activity 泄漏,不过 Looper 线程的消息队列中还是可能会有待处理
的消息,所以我们在 Activity 的 Destroy 时或者 Stop 时应该移除消息队列 MessageQueue 中的消息。
下面几个方法都可以移除 Message:
public final void removeCallbacks(Runnable r);

public final void removeCallbacks(Runnable r, Object token);

public final void removeCallbacksAndMessages(Object token);

public final void removeMessages(int what);

public final void removeMessages(int what, Object object);

Android内存泄露注意事项:
1.尽量避免使用 static 成员变量。

2.避免 override finalize()。

3.资源未关闭造成的内存泄漏。(对于使用了BraodcastReceiver,ContentObserver,File,游标 Cursor
,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,
造成内存泄漏)

4.一些不良代码造成的内存压力。(有些代码并不造成内存泄露,但是它们,或是对没使用的内存没进行有
效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存)

总结
1.对 Activity 等组件的引用应该控制在 Activity 的生命周期之内; 如果不能就考虑使用
getApplicationContext 或者 getApplication,以避免 Activity 被外部长生命周期的对象引用而泄露。

2.尽量不要在静态变量或者静态内部类中使用非静态外部成员变量(包括context ),即使要使用,也要考虑
适时把外部成员变量置空;也可以在内部类中使用弱引用来引用外部类的变量。

3.对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄
漏:

*将内部类改为静态内部类静态内部类中使用弱引用来引用外部类的成员变量*

Handler 的持有的引用对象最好使用弱引用,资源释放时也可以清空 Handler 里面的消息。比如在
Activity onStop 或者 onDestroy 的时候,取消掉该 Handler 对象的 Message和 Runnable.

4.在 Java 的实现过程中,也要考虑其对象释放,最好的方法是在不使用某对象时,显式地将此对象赋值为
null,比如使用完Bitmap 后先调用 recycle(),再赋为null,清空对图片等资源有直接引用或者间接引用的
数组(使用 array.clear() ; array = null)等,最好遵循谁创建谁释放的原则。

5.正确关闭资源,对于使用了BraodcastReceiver,ContentObserver,File,游标 Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销。

6.保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期。

Handler内存泄露分析和解决:
1、 Android角度
当Android应用程序启动时,framework会为该应用程序的主线程创建一个Looper对象。这个Looper对象包含一个简单的消息队列Message Queue,并且能够循环的处理队列中的消息。这些消息包括大多数应用程序framework事件,例如Activity生命周期方法调用、button点击等,这些消息都会被添加到消息队列中并被逐个处理。

另外,主线程的Looper对象会伴随该应用程序的整个生命周期。

然后,当主线程里,实例化一个Handler对象后,它就会自动与主线程Looper的消息队列关联起来。所有发
送到消息队列的消息Message都会拥有一个对Handler的引用,所以当Looper来处理消息时,会据此回调
[Handler#handleMessage(Message)]方法来处理消息。

2、 Java角度
在java里,非静态内部类 和 匿名类 都会潜在的引用它们所属的外部类。但是,静态内部类却不会。

总结:
虽然静态类与非静态类之间的区别并不大,但是对于Android开发者而言却是必须理解的。至少我们要清楚
,如果一个内部类实例的生命周期比Activity更长,那么我们千万不要使用非静态的内部类。最好的做法是
,使用静态内部类,然后在该类里使用弱引用来指向所在的Activity。

解决:
为了解决遇到的问题,我们要明确一点:静态内部类不会持有对外部类的引用。所以,我们可以把handler
类放在单独的类文件中,或者使用静态内部类便可以避免泄漏。

另外,如果想要在handler内部去调用所在的外部类Activity,那么可以在handler内部使用弱引用的方式指
向所在Activity,这样统一不会导致内存泄漏。

对于匿名类Runnable,同样可以将其设置为静态类。因为静态的匿名类不会持有对外部类的引用。

Looper、Handler、Message、Messageq关系:
加上以上分析,我们将之前分析结果串起来,就可以知道了某些东西: Looper.loop()不断地获取
MessageQueue中的Message,然后调用与Message绑定的Handler对象的dispatchMessage方法,最后,我们看到了handleMessage就在dispatchMessage方法里被调用的。

当我们调用handler.sendMessage(msg)方法发送一个Message时,实际上这个Message是发送到与当前线程绑定的一个MessageQueue中,然后与当前线程绑定的Looper将会不断的从MessageQueue中取出新的Message,调用msg.target.dispathMessage(msg)方法将消息分发到与Message绑定的handler.handleMessage()方法中。

一个Thread对应多个Handler 一个Thread对应一个Looper和MessageQueue,Handler与Thread共享Looper和MessageQueue。 Message只是消息的载体,将会被发送到与线程绑定的唯一的MessageQueue中,并且被与线程绑定的唯一的Looper分发,被与其自身绑定的Handler消费。

Android性能优化:
1.节制的使用Service
我们可以使用IntentService,当后台任务执行结束后会自动停止,避免了Service的内存泄漏。

2.当界面不可见时释放内存
当用户打开了另外一个程序,我们的程序界面已经不可见的时候,我们应当将所有和界面相关的资源进行释
放。重写Activity的onTrimMemory()方法,然后在这个方法中监听TRIM_MEMORY_UI_HIDDEN这个级别,一旦触发说明用户离开了程序,此时就可以进行资源释放操作了。

3.当内存紧张时释放内存
onTrimMemory()方法还有很多种其他类型的回调,可以在手机内存降低的时候及时通知我们,我们应该根据回调中传入的级别来去决定如何释放应用程序的资源

4.避免在Bitmap上浪费内存

5.使用优化过的数据集合

6.知晓内存的开支情况
使用枚举通常会比使用静态常量消耗两倍以上的内存,尽可能不使用枚举
任何一个Java类,包括匿名类、内部类,都要占用大概500字节的内存空间
任何一个类的实例要消耗12-16字节的内存开支,因此频繁创建实例也是会在一定程序上影响内存的
使用HashMap时,即使你只设置了一个基本数据类型的键,比如说int,但是也会按照对象的大小来
分配内存,大概是32字节,而不是4字节,因此最好使用优化后的数据集合

7.谨慎使用抽象编程
在Android使用抽象编程会带来额外的内存开支,因为抽象的编程方法需要编写额外的代码,虽然这些代码
根本执行不到,但是也要映射到内存中,不仅占用了更多的内存,在执行效率上也会有所降低。所以需要合
理的使用抽象编程。

8.尽量避免使用依赖注入框架

9.谨慎使用使用多个进程

10.高性能编码优化
都是一些微优化,在性能方面看不出有什么显著的提升的。使用合适的算法和数据结构是优化程序性能的最
主要手段。
a.避免创建不必要的对象
b.静态优于抽象
c.对常量使用static final修饰符
d.使用增强型for循环语法
e.多使用系统封装好的API
f.避免在内部调用Getters/Setters方法

11.布局优化技巧
a.重用布局文件
b.仅在需要时才加载布局

ListView详解:
直接继承自AbsListView,AbsListView继承自AdapterView,AdapterView又继承自ViewGroup。
Adpater在ListView和数据源之间起到了一个桥梁的作用

RecycleBin机制
RecycleBin机制是ListView能够实现成百上千条数据都不会OOM最重要的一个原因。RecycleBin是
AbsListView的一个内部类。

RecycleBin当中使用mActiveViews这个数组来存储View,调用这个方法后就会根据传入的参数来将ListView中的指定元素存储到mActiveViews中。

mActiveViews当中所存储的View,一旦被获取了之后就会从mActiveViews当中移除,下次获取同样位置的时候将会返回null,所以mActiveViews不能被重复利用。

addScrapView()用于将一个废弃的View进行缓存,该方法接收一个View参数,当有某个View确定要废弃掉的时候(比如滚动出了屏幕)就应该调用这个方法来对View进行缓存,RecycleBin当中使用mScrapV
iews和mCurrentScrap这两个List来存储废弃View。

getScrapView 用于从废弃缓存中取出一个View,这些废弃缓存中的View是没有顺序可言的,因此
getScrapView()方法中的算法也非常简单,就是直接从mCurrentScrap当中获取尾部的一个scrap view进行
返回。

我们都知道Adapter当中可以重写一个getViewTypeCount()来表示ListView中有几种类型的数据项,而
setViewTypeCount()方法的作用就是为每种类型的数据项都单独启用一个RecycleBin缓存机制。

View的流程
分三步,onMeasure()用于测量View的大小,onLayout()用于确定View的布局,onDraw()用于将
View绘制到界面上。

AsyncTask
首先从Android3.0开始,系统要求网络访问必须在子线程中进行,否则网络访问将会失败并抛出
onPreExecute、doInBackground、onProgressUpdate、onPostExecute
AsyncTask有两个线程池:SerialExecutor和THREAD_POOL_EXECUTOR。前者是用于任务的排队,默认是串行的线程池:后者用于真正的执行任务。AsyncTask还有一个Handler,叫InternalHandler,用于将执行环境从线程池切换到主线程。AsyncTask内部就是通过InternalHandler来发送任务执行的进度以及执行结束等消息。

关于线程池,AsyncTask对应的线程池ThreadPoolExecutor都是进程范围内共享的,都是static的,所以是
AsyncTask控制着进程范围内所有的子类实例。由于这个限制的存在,当使用默认线程池时,如果线程数超
过线程池的最大容量,线程池就会爆掉(3.0默认串行执行,不会出现这个问题)。针对这种情况。可以尝试
自定义线程池,配合AsyncTask使用。

ANR排错一般有三种类型
1.主要是类型按键或触摸事件在特定时间内无响应

2.BroadcastReceiver在特定时间内无法处理完成

3.小概率事件 Service在特定的时间内无法处理完成

导致ANR 在主线程执行以下操作:
1.高耗时的操作,如图像变换

2.磁盘读写,数据库读写操作

3.大量的创建新对象

如何避免ANR:
1.UI线程尽量只做跟UI相关的工作

2.耗时的操作(比如数据库操作,I/O,连接网络或者别的有可能阻塞UI线程的操作)把它放在单独的线程处理

3.尽量用Handler来处理UIThread和别的Thread之间的交互

ART和Dalvik区别:
Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik应用作为独立的Linux进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。

Dalvik是依靠一个Just-In-Time(JIT)编译器去解释字节码

ART则完全改变了这套做法,在应用安装的时候就预编译字节码到机器语言,这一机制叫Ahead-Of-Time
(AOT)编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快

Android关于OOM的解决方案
出现OOM的原因:
1.加载对象过大

2.相应资源过多,来不及释放

如何解决:
1.在内存引用上做些处理,常用的有软引用、强化引用、弱引用

2.在内存中加载图片时直接在内存中作处理,如边界压缩

3.动态回收内存

4.优化Dalvik虚拟机的堆内存分配

5.自定义堆内存大小

Android几种进程:
1.前台进程,即与用户正在交互的Activity或者Activity用到的Service等,如果系统内存不足时前台进程是最后被杀死的

2.可见进程:可以是处于暂停状态(onPause)的Activity或者绑定在其上的Service,即被用户可见,但由于
失去了焦点而不能与用户交互

3.服务进程:其中运行着使用startService方法启动的Service,虽然不被用户可见,但是却是用户关心的,例如用户正在非音乐界面听的音乐或者正在非下载页面自己下载的文件等;当系统要空间运行前两者进程
时才会被终止

4.后台进程:其中运行着执行onStop方法而停止的程序,但是却不是用户当前关心的,例如后台挂着的QQ,这样的进程系统一旦没了有内存就首先被杀死

5.空进程:不包含任何应用程序的程序组件的进程,这样的进程系统是一般不会让他存在的

如何避免后台进程被杀死
1.调用startForegound,让你的Service所在的线程成为前台进程

2.Service的onStartCommond返回START_STICKY或START_REDELIVER_INTENT

3.Service的onDestroy里面重新启动自己

APP启动过程:
* Zygote进程孵化出新的应用进程后,会执行ActivityThread类的main方法.在该方法里会先准备好Looper
和消息队列,然后调用attach方法将应用进程绑定到ActivityManagerService,然后进入loop循环,不断地
读取消息队列里的消息,并分发消息。

  • ActivityThread的main方法执行后,应用进程接下来通知ActivityManagerService应用进程已启动,ActivityManagerService保存应用进程的一个代理对象,这样ActivityManagerService可以通过这个代理对
    象控制应用进程,然后ActivityManagerService通知应用进程创建入口Activity的实例,并执行它的生命周
    期方法

MVP
MVP模式将Controller改名为Presenter,同时改变了通信方向。
各部分之间的通信,都是双向的
View和Model不发生联系,都通过Presenter传递
View非常薄,不部署任何业务逻辑,称为”被动视图”(Passive View),即没有任何主动性,而Presenter非常厚,所有逻辑都部署在那里。

优点:
1.降低耦合度,实现了Model和View真正的完全分离,可以修改View而不影响Modle

2.模块职责划分明显,层次清晰

3.隐藏数据

4.Presenter可以复用,一个Presenter可以用于多个View,而不需要更改Presenter的逻辑(当然是在View的改动不影响业务逻辑的前提下)

5.代码灵活性

缺点:
1.由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁

2.如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了。

3.额外的代码复杂度及学习成本。

在MVP模式里通常包含4个要素:
1.View :负责绘制UI元素、与用户进行交互(在Android中体现为Activity);

2.View interface :需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方
便进行单元测试;

3.Model :负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合);

4.Presenter :作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。

Android开机过程:
1.BootLoder引导,然后加载Linux内核.

2.0号进程init启动.加载init.rc配置文件,配置文件有个命令启动了zygote进程

3.zygote开始fork出SystemServer进程

4.SystemServer加载各种JNI库,然后init1,init2方法,init2方法中开启了新线程ServerThread.

5.在SystemServer中会创建一个socket客户端,后续AMS(ActivityManagerService)会通过此客户端和
zygote通信

6.ServerThread的run方法中开启了AMS,还孵化新进程ServiceManager,加载注册了一溜的服务,最后一句话
进入loop 死循环

7.run方法的SystemReady调用resumeTopActivityLocked打开锁屏界面

EventBus
EventBus是一款针对Android优化的发布/订阅(publish/subscribe)事件总线
事件传递既可用于 Android 四大组件间通讯,也可以用户异步线程和主线程间通讯等等。
传统的事件传递方式包括:Handler、BroadCastReceiver、Interface 回调,相比之下 EventBus 的优点是
代码简洁,使用简单,并将事件发布和订阅充分解耦。

常见的面向对象设计原则
1.单一职责原则 SRP 一个类应该仅有一个引起它变化的原因。

2.开放关闭原则 OCP 一个类应该对外扩展开放,对修改关闭。

3.里氏替换原则 LSP 子类型能够替换掉它们的父类型。

4.依赖倒置原则 DIP 要依赖于抽象,不要依赖于具体类,要做到依赖倒置

5.接口隔离原则 ISP 不应该强迫客户依赖于他们不用的方法。

6.最少知识原则 LKP 只和你的朋友谈话。

7.面向接口编程/优先使用组合,而非继承/一个类需要的数据应该隐藏在类的内部/类之间应该零耦合,或
者只有传导耦合/在水平方向上尽可能统一地分布系统功能

设计模式

1.单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
饿汉式: private static Singleton uniqueInstance = new Singleton();
懒汉式 private static Singleton uniqueInstance = null;

饿汉式是线程安全的,因为虚拟机保证只会装载一次,在装载类的时候是不会发生并发的。
不加同步的懒汉式是线程不安全的,解决办法:
public static synchronized Singleton getInstance(){}
但这样会降低整个访问的速度,而且每次都要判断。可以用双重检查加锁。

2.简单工厂
简单工厂的本质是选择实现。
简单工厂的功能:
不仅可以利用简单工厂来创建接口,也可以用简单工厂来创造抽象类,甚至是一个具体的实例。
静态工厂:
没有创建工厂实例的必要,把简单工厂实现成一个工具类,直接使用静态方法。
万能工厂:
一个简单哪工厂可以包含很多用来构造东西的方法,这些方法可以创建不同的接口、抽象类或者是类实例。

优点:
1.帮助封装

2.解耦

缺点:
1.可能增加客户端的复杂度

2.不方便扩展子工厂

3.观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变的时候,所有依赖于它的对象都将得到通
知,并自动更新在Android中,我们往ListView添加数据后,都会调用Adapter的notifyDataChanged()方法,其中使用了观察者模式。

当ListView的数据发生变化时,调用Adapter的notifyDataSetChanged函数,这个函数又会调用
DataSetObservable的notifyChanged函数,这个函数会调用所有观察者(AdapterDataSetObserver)的
onChanged方法,在onChanged函数中又会调用ListView重新布局的函数使得ListView刷新界面。

4.代理模式
代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用
模式的使用场景:
就是一个人或者机构代表另一个人或者机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一
个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

优点
给对象增加了本地化的扩展性,增加了存取操作控制

缺点
会产生多余的代理类

5.适配器模式
将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些
类可以一起工作。

功能:
进行转换匹配,目的是复用已有的功能,而不是来实现新的接口。在适配器里实现功能,这种适配器称为智
能适配器。

优点:
更好的复用性
更好的扩展性

缺点:
过多的使用适配器,会让系统非常零乱,不容易整体进行把握。

Java基础
Java面向对象的三个特征与含义:
继承,封装,多态

反射机制
JAVA反射机制是在运行状态中, 对于任意一个类, 都能够知道这个类的所有属性和方法; 对于任意一个对象
, 都能够调用它的任意一个方法和属性; 这种动态获取的信息以及动态调用对象的方法的功能称为java语言
的反射机制.

主要作用有三:
1.运行时取得类的方法和字段的相关信息。

2.创建某个类的新实例(.newInstance())

3.取得字段引用直接获取和设置对象字段,无论访问修饰符是什么。

用处如下:
1.观察或操作应用程序的运行时行为。

2.调试或测试程序,因为可以直接访问方法、构造函数和成员字段。

3.通过名字调用不知道的方法并使用该信息来创建对象和调用方法。

泛型的优缺点
优点:
1.使用泛型类型可以最大限度地重用代码、保护类型的安全以及提高性能。

2.泛型最常见的用途是创建集合类。

缺点:
在性能上不如数组快。

解析XML的几种方式:
DOM

SAX:SAX是一个解析速度快并且占用内存少的xml解析器,非常适合用于android等移动设备
1. 解析效率高,占用内存少

2.可以随时停止解析

3.不能载入整个文档到内存

4.不能写入xml

Sax的工作原理简单的说,就是对文档进行顺序扫描,扫描到文档(document)开始与结束,扫描到元素(element)开始、结束等地方时调用事件处理
处理函数做相应动作,然后继续扫描,直到文档结束。

PULL

Java集合总结
集合

    Collection(单列集合)        List(有序,可重复)            ArrayList                底层数据结构是数组,查询快,增删慢                线程不安全,效率高            Vector                底层数据结构是数组,查询快,增删慢                线程安全,效率低            LinkedList                底层数据结构是链表,查询慢,增删快                线程不安全,效率高        Set(无序,唯一)            HashSet                底层数据结构是哈希表                哈希表依赖两个方法:hashCode()和equals()(保证元素唯一性)                执行顺序:                    首先判断hashCode()值是否相同                        是:继续执行equals(),看其返回值                            是true:说明元素重复,不添加                            是false:就直接添加到集合                        否:直接添加到集合                最终:                    自动生成hashCode()和equals()即可                LinkedHashSet                    底层数据结构由链表和哈希表组成                    由链表保证元素有序                    由哈希表保证元素唯一            TreeSet                底层数据结构是红黑树。(是一种自平衡的二叉树)                如何保证元素唯一性呢?                    根据比较的返回值是否为0来决定                如何保证元素的排序呢?                    两种方式:                        自然排序(元素具备可比较性)                            让元素所属的类实现Comparable接口                        比较器排序(集合具备比较性)                            让集合接收一个Comparator的实现类对象    Map(双列集合)        A:Map集合的数据结构仅仅针对键有效,与值无关。        B:存储的是键值对形式的元素,键唯一,值可重复        HashMap            底层数据结构是哈希表,线程不安全,效率高                哈希表依赖两个方法:hashCode()和equals()                执行顺序:                    首先判断hashCode()值是否相同                        是:继续执行equals(),看其返回值                            是true:说明元素重复,不添加                            是false:就直接添加到集合                        否:直接添加到集合                最终:                    自动生成hashCode()和equals()即可            LinkedHashMap                底层数据结构由链表和哈希表组成                    由链表保证元素有序                    由哈希表保证元素唯一        Hashtable            底层数据结构是哈希表,线程安全,效率低        TreeMap            底层数据结构是红黑树。(是一种自平衡的二叉树)                如何保证元素的唯一性呢?                    根据比较的返回值是否为0来决定                如何保证元素的排序性呢?                    两种方式:                        自然排序(元素具备比较性)                            让元素所属的类实现Comparable接口                        比较器排序(集合具备比较性)                            让集合接收一个Comparator的实现类对象
0 0
原创粉丝点击