Android面试整理-设计模式
单例模式
答案参考自:
- 单例模式 | 菜鸟教程 (runoob.com)
- DCL之单例模式_浦江之猿的博客-CSDN博客_dcl单例模式
- 《Android源码设计模式解析与实战》
意图: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
关键代码: 构造函数是私有的。
优点:
在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
避免对资源的多重占用(比如写文件操作)。
实现代码
饿汉式
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。1
2
3
4
5
6
7public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}DCL
优点:资源利用率高。
缺点:第一次加载的时候反应稍慢。
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class Singleton {
private volatile static Singleton singleton;
private Singleton () {}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}使用
volatile
是为了防止指令重排序问题,避免第8行代码被分配了控件但是值为null
的情况。两次 if 语句的作用:
第一个 if 语句
防止在变量实例化后仍有线程获取锁,造成性能的开销。
第二个 if 语句
防止变量的重复实例化。
静态内部类
1
2
3
4
5
6
7
8
9public class Singleton {
private Singleton() {}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
}第一次加载
Singleton
类的时候并不会初始化instance
,只有在第一次调用Singleton.getInstance
方法时才会初始化instance
。因此第一次调用getInstance
方法时也会导致虚拟机加载SingletonHolder
类,这种方式不仅确保了线程安全,也能够保证单例模式对象的唯一性,同时也延迟了单例的实例化。
Android源码中的单例模式
LayoutInflater
类。
在ContextImpl
类中,LayoutInflater
的获取被写进了该类的static{}
,即静态语句块中。表示了只有在该类被加载的时候才会被执行一次且只会执行一次,保证了实例的唯一性。
建造者模式
答案参考自:
意图: 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
何时使用: 一些基本部件不会变,而其组合经常变化的时候。
优点:
建造者独立,易扩展。
便于控制细节风险。
缺点: 产品必须有共同点,范围有限制。
注意事项: 与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
用例:
调用者不需要知道Director
类中方法的具体实现,而Director
类同样也不需要知道Builder
类中的具体实现。
当我们需求发生变动时,只需要单独修改Director
或者Builder
类的内部细节即可,不会影响到其他类的实现。
Android中的建造者模式
Java
的StringBuilder
。OkHttp
中的OkHttpClient
。Android
中的AlterDialog
。
责任链模式
答案参考自:
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
意图: 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
主要解决: 职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
何时使用: 在处理消息的时候需要过滤很多道。
优点:
降低耦合度。它将请求的发送者和接收者解耦。
简化了对象。使得对象不需要知道链的结构。
增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。
增加新的请求处理类很方便。
缺点:
不能保证请求一定被接收。
系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
可能不容易观察运行时的特征,有碍于除错。
Android中类似责任链模式的使用
OkHttp
的拦截器。- View事件分发的
dispatchTouchEvent
以及dispatchTransformedTouchEvent
方法。
观察者模式
答案参考自:
定义一个被观察者和多个观察者,每当被观察者变化,所有观察者都会得到通知。
要点: P用一个list把观察者保存起来,并提供add和remove观察者,在被观察者变化的时候就遍历并调用list里观察者的方法。核心就是一个list遍历。
优点:
观察者和被观察者解耦
增强灵活性,解耦
缺点: 由于java代码的顺序执行,要考虑被察者的执行效率,多个观察者需要及时响应就得考虑异步的问题了。
Android源码中的观察者模式
RecyclerView
的notifyDataChanged
方法。当数据集发生变化,调用RecyclerView.Adapter的notifyDataSetChanged()时,发生变化的地方就会刷新,这个过程就涉及到了Java中的观察者模式。
在RecyclerView的使用中,最重要的两个类应该就是RecyclerView和RecyclerView.Adapter了。
RecyclerView内部维持了一个Observer(RecyclerViewDataObserver)。
Adapter内部维持了一个Observable(AdapterDataObservable)。
我们调用setAdapter(Adapter adapter)时,Adapter内的Observable会持有RecyclerView内部的Observer;当调用notifyDataSetChanged()时,Observable持有的Observer会依次调用onChanged()方法,而RecyclerView持有的Observer的onChanged()方法中实现了UI刷新。