单例模式(1)
优点:1.减少内存开支
2.减少性能开销
3.避免对一个资源的多重占用
4.便于全局访问
缺点:
1.没有接口扩展困难
2.单例持有Context容易造成内存泄露,所以推荐使用Application的Context。
饿汉式(不管吃不吃得下,先把碗装满)
public class HungrySingleton{
private static final HungrySington intance = new HungrySingleton();
private HungrySingleton(){}
public static HungrySington getIntance(){
return intance;
}
}
1.线程安全。利用了类的加载机制,加载初始化静态变量,且被只会执行一次,且JVM会利用锁来同步多个线程对同一个类的初始化。这样就保证了构造方法只会调用一次。
2.不能懒加载。无论是否使用都会去初始化实例。
懒汉式(需要的时候再去获取)
public class LazySingleton{
private static final LazySingleton intance = null;
private LazySingleton(){}
public static LazySingleton getIntance(){
if(intance == null){
intance = new LazySingleton();
}
return intance;
}
}
优点是可以实现延迟加载。在类初次加载的时候,由于只是声明了这个静态的对象,但不会自动初始化 instance对象,所以称为懒汉式
缺点是线程不安全。多线程并发无法保证唯一的实例改进策略:需要保证线程安全
双重检查锁(在需要创建对象的时候再进行同步锁操作,即如果实例对象已经创建,那么多线程的状态下可以同时获取到实例对象)
public class DoubleCheckSingleton{
private volatile static DoubleCheckSingleton intance;
private DoubleCheckSingleton(){}
public static DoubleCheckSingleton getInstance(){
if(instance == null){
synchronized(DoubleCheckSingleton .class){
if(instance == null){
intance = new DoubleCheckSingleton();
}
return intance;
}
// 这种策略通过两次判断后才创建单例,解决了安全的问题,但是由于 instance = new Singleton();
//这个初始化是非原子性的操作,JVM中创建对象分为3步(1,给实例分配内存,2,调用构造函数初始化成员字段,3,实例对象指向分配的内存空间 23不能保证顺序执行)。
//添加 volatile 关键字,禁止指令重排序。
静态内部类(得益于类加载机制)
public class Singleton{
public static Singleton getIntance(){
return SingletonHolder.instance;
}
private Singleton(){}
private static class SingletonHolder{
private static final Singleton instance = new Singleton ();
}
}
当第一次加载Singleton类时不会初始化instance,只有在调用了getSingleton方法的时候才会触发instance被初始化。因此,第一次调用get'Singleton方法会导致虚拟机加载SingletonHolder类,这种方式不仅能确保线程安全,也能保证单例对象的唯一性,同时也延迟了单例的实例化。所以单例模式推荐使用这种方式实现)
Builder模式(2)
定义
将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。
适用于:对象初始化复杂、产品类复杂不同调用顺序产生最终类型不同、方法不同调用顺序产生最终结果不同。链式调用直观。
优点
良好封装,使外部不必知道内部组成细节。
建造者独立,容易拓展。
缺点
产生多余builder对象以及director对象,消耗内存。
运用场景:
AlertDialog.builder
OKHttp中的Request.Builder
我们要创建的对象是很复杂的,有很多参数,链式调用每一个方法,这样不仅写起来简单,而且读起来也很有逻辑感。
工厂模式(3)
定义
定义一个创建对象的接口,让子类决定实例化哪个类。
优点
工厂模式完全符合依赖倒置设计原则,高层模块只需要知道产品的抽象类,其他的实现都不需要关心。
良好的封装性,代码结构清晰。扩展性好。
缺点
每次为工厂方法模式添加新的产品时就要编写一个新的产品类。同时还要引入抽象层,这必然会导致类结构的复杂化,所以,在某些情况比较简单时,是否要使用工厂模式,需要设计者权衡利弊了。
运用场景
在任何需要生成复杂对象的地方,都可以使用工厂方法模式。复杂对象适合使用工厂模式。
android中onCreate()等生命周期。A和B的Acitivty中设置不同的view都是通过setContentView()返回给framerwork处理。这里view的不同就是工厂模式的具体产品的实现。
android的bitmap中常用的BitmapFactory类,根据传入的参数来实例化不同的对象返回到Bitmap。
工厂模式主要分为四大模块:
1.抽象工厂,其为工厂方法模式的核心。
2.具体工厂,其实现了具体的业务逻辑。
3.抽象产品,是工厂方法模式所创建的产品的父类。
4.具体产品,为实现抽象产品的某一个具体产品对象。
工厂模式和构建者模式区别就是:工厂方法模式注重的是整体对象的创建方法,而建造者模式注重的是部件构建的过程。比如造车工厂模式强调是宝马还是奔驰,构造者模式强调发动机是4缸还是6缸。
适配器模式(4)
定义
适配器模式吧一个类的接口变换成客户端锁期待的另一种接口,从而使原本因为接口不匹配而无法在一起工作的两个类能够在一起工作。
适配器模式有两种:
类适配器
先建目标接口,需要的是5V的电压
public interface FiveVolt {
public int getVolt5();
}
然后是插座的电压220v
public class Volt220 {
public int getVolt220(){
return 220;
}
}
适配器
public class VoltAdapter extends Volt220 implements FiveVolt {
@Override
public int getVolt5() {
return 5;
}
}
对象适配器
适配器的构造函数传入需要适配的对象。
public interface FiveVolt {
public int getVolt5();
}
需要适配的:
public class Volt220 {
public int getVolt220(){
return 220;
}
}
适配器:
public class VoltAdapter implements FiveVolt {
Volt220 volt220 ;
public VoltAdapter(Volt220 volt220) {
this.volt220 = volt220;
}
@Override
public int getVolt5() {
return 5;
}
public int getVolt220(){
return volt220.getVolt220();
}
}
客户端调用:
public class Client {
public static void main(String[] args) {
VoltAdapter voltAdapter = new VoltAdapter(new Volt220());
System.out.println(voltAdapter.getVolt5());
}
}
对象适配器直接把要被适配的对象传递到Adapter中,使用组合的形式实现接口兼容。比类适配器更加灵活,而且不会暴露被适配对象中的方法。
类适配器由于继承了被适配的类,所以这个适配器会拥有被适配对象的方法,导致适配器出现一些奇怪的方法。
优点
提高了灵活性,更换适配器就能达到不同的效果。而且不用修改原有代码。
提高现有类和系统类的复用性,适配器能让一个类有更广泛的用途。
类适配器可以重写继承的被适配者的一些方法来增加灵活性
对象适配器可以传入不同的对象把不同的对象都适配到同一个接口。
缺点
过多的使用适配器,会让系统变得非常凌乱,不易整体把握。明明调用A接口,却被适配成B接口。
java不能多继承,所以类适配器只能适配一个被适配类。
运用场景
listview中adapter的getview()定义
引用网上的一个例子:笔记本电脑电源一般用的都是5V电压,但是我们的家用电是220V,我们要让笔记本充上电,最好的办法应该是通过一个工具把220V的电压转换成5V,这个工具就是适配器
观察者模式(5)
定义
定义对象间的一种一个对多的依赖关系,当一个对象的状态发送改变时,所以依赖于它的对象都得到通知并被自动更新。
优点 :解除观察者与主题之间的耦合。让耦合的双方都依赖于抽象,而不是依赖具体。从而使得各自的变化都不会影响另一边的变化。
易于扩展,对同一主题新增观察者时无需修改原有代码。
缺点 :程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现。
可能会引起多余的数据通知。
应用场景
当一个对象的改变需要通知其它对象改变时,而且它不知道具体有多少个对象有待改变时。
当一个对象必须通知其它对象,而它又不能假定其它对象是谁
跨系统的消息交换场景,如消息队列、事件总线的处理机制。
Android中的源码分析
控件中Listener监听方式
广播接收者BroadcastReceiver
一些著名的第三方事件总线库,比如RxJava、EventBus
责任链模式(6)
将 多个对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
场景
①事件分发机制
②在OKhttp的拦截器中,发起一个网络请求,我们将请求封装成Request对象。交给拦截器中的某个拦截器,该拦截器处理完自己的工作之后,根据具体业务逻辑判断是否需要交给下一个拦截器。如果不需要,就将处理结果Response对象返回给请求者!
代理模式(7)
定义
目标对象为其对象提供一种代理对象,其他对象通过代理对象来控制对目标对象的访问。
角色划分
Proxy:代理对象,其他对象直接控制的对象。
Subject::目标接口,目标对象的抽象。
RealSubject:具体目标对象,目标接口的实现,即真正控制的对象。
优点
代理作为调用着和真实对象的中间层,降低了模块间和系统的耦合性。比如租房子通过中介找到房东,降低了租客和房东的耦合性。
缺点
多了一个中间层,所以会增加调用响应的时间。
MVP架构的P层就是代理
ActivityManagerProxy
Binder机制
java设计原则
1、单一职责原则
一个类只负责一种职责,只有这种职责的改变会导致这个类的变更。绕口一点的正统说法:不要存在多于一个原因导致类变更。
2、开闭原则
尽量通过扩展来面对需求的更改或者系统的变化,尽量不要对原有内容修改。
3、里氏替换原则
只要父类出现的地方,都可以用子类替换,并且不会对程序造成影响,在实现上来说就是子类不要覆盖父类的非抽象方法,但可以重载。
4、依赖倒置原则: 高层模块不应该依赖低层模块,二者都应该依赖其抽象,翻译一下就是面向接口编程;接口一般是行为的集合,也就是尽可能的对行为抽象。抽象不应该依赖细节,细节应该依赖抽象。
5、接口隔离原则
翻译一下就是接口的功能尽可能单一,接口本质上是两个类之间关系的纽带,关系中不需要有的,在接口中不应该体现。如:A 通过接口I依赖B,假如接口I中有A 不需要的方法,那么这个接口就是不合理的,B必须要实现这个不需要的方法,徒劳无功。
6、迪米特法则(最少知道原则)
也就是说一个对象要对其他对象保持尽可能少的了解,即低耦合性,低耦合可以最大限度的保证代码的可复用性。这个实际上是针对被依赖的类来说的,对于被依赖的类,尽可能的将复杂的逻辑封装起来,对外只提供public方法,外部不需要知道内部的逻辑。
网友评论