设计模式。

单例模式

整个项目有且只有一个实例

懒汉式

普通写法

public class Singleton {
 
    /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
    private static Singleton instance = null;
 
    /* 私有构造方法,防止被实例化 */
    private Singleton() {
    }
 
    /* 1:懒汉式,静态工程方法,创建实例 */
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

优点:延迟加载(需要的时候才去加载)

缺点: 线程不安全,在多线程中很容易出现不同步的情况,如在数据库对象进行的频繁读写操作时。

加同步锁

public class Singleton {
 
    /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
    private static Singleton instance = null;
 
    /* 私有构造方法,防止被实例化 */
    private Singleton() {
    }
 
    /*懒汉式变种,解决线程安全问题**/
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

或者

public class Singleton {
 
    /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
    private static Singleton instance = null;
 
    /* 私有构造方法,防止被实例化 */
    private Singleton() {
    }
 
 	 /*懒汉式变种,解决线程安全问题**/
     public static Singleton getInstance() {
        synchronized (Singleton.class) {
            if (instance == null) {
                instance = new Singleton();
            }
        }
        return instance;
    }
}

优点:解决了线程不安全的问题。

缺点:效率有点低,每次调用实例都要判断同步锁

双重检验锁

优化上面因为每次调用实例都要判断同步锁的问题

public class Singleton {
 
    /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
    private static Singleton instance = null;
 
    /* 私有构造方法,防止被实例化 */
    private Singleton() {
    }
 
    /*双重锁定:只在第一次初始化的时候加上同步锁*/
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

优点:解决了每次调用实例都要判断同步锁的问题

缺点:在JVM编译的过程中会出现指令重排的优化过程,这就会导致当 instance实际上还没初始化,就可能被分配了内存空间,也就是说会出现 instance !=null 但是又没初始化的情况,这样就会导致返回的 instance 不完整

volatile

public class Singleton {
 
    /* volatile 防止指令重排 */
    private static volatile Singleton instance = null;
 
    /* 私有构造方法,防止被实例化 */
    private Singleton() {
    }
 
    /*双重锁定:只在第一次初始化的时候加上同步锁*/
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

饿汉式

直接内部初始化,不存在线程安全问题

public class Singleton {
    private static final Singleton instance = new Singleton();//饿汉式,直接先实例化
 
    private Singleton(){
 
    }
    
    public static Singleton getInstance(){
        return instance;
    }
}

问题:单例模式能保证对象的唯一性吗?

不能

例如:

public class Singleton implements Serializable {
 
    /* volatile 防止指令重排 */
    private static volatile Singleton instance = null;
 
    /* 私有构造方法,防止被实例化 */
    private Singleton() {
    }
 
    /*双重锁定:只在第一次初始化的时候加上同步锁*/
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

一般来说, 一个类实现了 Serializable接口, 我们就可以把它往内存写,再从内存里读出而”组装”成一个跟原来一模一样的对象. 不过当序列化遇到单例时,这里边就有了个问题: 从内存读出而组装的对象破坏了单例的规则. 单例是要求一个JVM中只有一个类对象的, 而现在通过反序列化,一个新的对象克隆了出来。那怎么来维护单例模式呢?这就要用到readResolve()方法或者使用枚举实现单例模式了。但是我们先来讲讲序列化和反序列化。

序列化和反序列化

  • 序列化:把Java对象转换为字节序列的过程。
public static void serialize( ) throws IOException {

    Student student = new Student();
    student.setName("CodeSheep");
    student.setAge( 18 );
    student.setScore( 1000 );

    ObjectOutputStream objectOutputStream = 
        new ObjectOutputStream( new FileOutputStream( new File("student.txt") ) );
    objectOutputStream.writeObject( student );
    objectOutputStream.close();
    
    System.out.println("序列化成功!已经生成student.txt文件");
}
  • 反序列化:把字节序列恢复为Java对象的过程。
public static void deserialize(  ) throws IOException, ClassNotFoundException {
    ObjectInputStream objectInputStream = 
        new ObjectInputStream( new FileInputStream( new File("student.txt") ) );
    Student student = (Student) objectInputStream.readObject();
    objectInputStream.close();
    
    System.out.println("反序列化结果为:" + student );
}

问题:readResolve()方法为什么就可以解决序列化破坏单例的问题呢?

大家看反序列化的过程,主要用到的类就是ObjectInputStream,进入objectInputStream.readObject()方法:
在这里插入图片描述
返回的obj对象,就是ObjectInputStream的readObject0返回的对象。

进入ObjectInputStream.readObject0(),

在这里插入图片描述
switch语句对枚举或者Object类都有对应的反序列化机制。

checkResolve:检查对象,并替换

readOrdinaryObject:读取二进制对象,我们先进入readOrdinaryObject()方法

在这里插入图片描述
可以看到,readOrdinaryObject()方法是通过desc.isInstantiable() 来判断是否需要new一个对象,如果返回true,方法通过反射的方式调用无参构造方法新建一个对象,否则,返回空。 那我们进入isInstantiable()方法

在这里插入图片描述
cons != null是判断类的构造方法是否为空,我们大家应该知道,Class类的构造方法肯定不为空,显然isInstantiable()返回true,也就是说,一定会new 一个对象,且被obj接收。

我们回到readOrdinaryObject()方法,查看初始化完成后的操作。
在这里插入图片描述
要敲黑板了,这里就是单例类中定义readResolve就可以解决问题的关键所在!

若"obj != null &&handles.lookupException(passHandle) == null &&desc.hasReadResolveMethod()"这条语句返回true,就会调用Object rep = desc.invokeReadResolve(obj) 这条语句。

我们进入hasReadResolveMethod()方法

在这里插入图片描述

"readResolveMethod != null "的判断,我们深究进去,readResolveMethod是从哪里读取进来的呢?

在这里插入图片描述
对readResolveMethod赋值, 通过反射获得类中名为readResolve的方法

我们再回到readOrdinaryObject()方法,查看赋值操作

在这里插入图片描述
在这里插入图片描述

也就是说! 若目标类有readResolve方法,那就通过反射的方式调用要被反序列化的类中的readResolve方法,返回一个对象,然后把这个新的对象复制给之前创建的obj(即最终返回的对象)

那被反序列化的类中的readResolve 方法里是什么?就是直接返回我们的单例对象

问题:为什么枚举也可以解决序列化破坏单例的问题?

Java规范字规定,每个枚举类型及其定义的枚举变量在JVM中都是唯一的,因此在枚举类型的序列化和反序列化上,Java做了特殊的规定。在序列化的时候Java仅仅是将枚举对象的name属性输到结果中,反序列化的时候则是通过java.lang.Enum的valueOf()方法来根据名字查找枚举对象

当反序列化时,会执行readEnum()方法

在这里插入图片描述

进入readEnum()方法,发现返回对象的赋值是通过Enum.valueOf((Class)cl, name)得到的,

在这里插入图片描述

进入Enum.valueOf((Class)cl, name)方法,发现反序列化是通过这个name来查找对应的枚举类型的。因此反序列化后的实例也会和之前被序列化的对象实例相同。

在这里插入图片描述

观察者模式

Android本地广播

优点:在 BroadcastReceiver 中的 onReceive 方法中可以获得 Context、Intent 参数。持有这两个参数便可以调用许多 android sdk 中的方法,这一优势另外两种方式很难弥补的。无论是 EventBus 还是观察者,需要获得 Context 的话,往往都需要进行复杂的参数传递或者是依赖注入

缺点:

  • 广播是重量级的、消耗资源较多的方式
  • 使用 BroadcastReceiver 等非 Activity 组件的 Context启动 Activity 也有可能造成隐蔽的错误:当使用非 Activity 组件的 Context 启动 Activity 时,如果不指定 flag 的话,默认会创建一个新的 task;而使用 Activity 的 Context 并且不指定 flag 的话,默认会使用原 task

当我们需要同 android 交互的时候,广播提供的便捷性抵消掉了它过多的资源消耗

但是对于不需要同 android 交互或是只做很少的交互的时候,使用广播往往是一种浪费

EventBus

优点:可继承、优先级、粘滞,是 EventBus 比之于广播、观察者等方式最大的优点

  • 调度灵活,不依赖于 Context,使用时无需像广播一样关注 Context 的注入与传递。
  • 父类对于通知的监听和处理可以继承给子类,这对于简化代码至关重要。
  • 不会造成接口的膨胀
  • 通知的优先级,能够保证 Subscriber 关注最重要的通知。
  • 粘滞事件 (sticky events) 能够保证通知不会因 Subscriber 的不在场而忽略

缺点:基于反射实现的,所以混淆时需要配置其不混淆,安全性不高。

问题:EventBus中post和postSticky的区别?

post:注册就接收,没注册就不接收

postSticky:没注册,消息保留,等注册了依然会接收

粘性事件实现原理

在这里插入图片描述
发送粘性事件时,将粘性事件放到了一个ConcurrentHashMap中保存,然后又调用了post来发送消息,可以保证消息的正常发送。

但是在这里我们并没有看到eventbus是如何在目标activity创建之后,发送消息的。其实奥秘在activity注册EventBus的那部分:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
原来,在注册时,检测到存在粘性事件(具体存放位置是粘性事件发布时,加入的粘性事件池)时,就直接对消息进行发布了,这就是为什么可以在activity创建前发布粘性事件,当创建时可以接收到消息的原理

你可能还想问,当发送粘性事件时,目标activity已经创建了,那会发生什么呢?会收到两次重复的消息吗?答案显然是否定的,因为在目标activity已经创建后,不再执行register函数,自然也没法出现两条重复的信息,假如你不小心写了两次EventBus.getDefault().register(this),也没关系,因为EventBus在类注册后,就不再重复注册了。

观察者

定义对象间的一种一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都能得到通知并被自动更新

观察者模式核心就一个点,记住这个点你就能理解并记忆:用一个list把观察者保存起来,并提供add和remove观察者,在被观察者变化的时候就遍历并调用list里观察者的方法。核心就是一个list遍历。

RecyclerView和ListView中的Adapter就采用了观察者模式。

优点:实现比较简单

缺点:

  • 难以控制通知的优先级
  • 如果观察者不在场,通知会遗漏
  • 可能会造成接口的膨胀。特别是当程序要求大量形式各异的通知,而程序员有没有做出良好的抽象时,代码中会包含大量的接口,接口数量的增长又会带来命名、注释等等一大堆问题。本质上说观察者要求程序员从零开始实现事件的产生、分发与处理过程,这就要求参与者必须对整个通知过程有着良好的理解。当程序代码适量时,这是一个合理的要求,然而当程序太大时,这将成为一种负担

情景1:
有一种短信服务,比如天气预报服务,一旦你订阅该服务,你只需按月付费,付完费后,每天一旦有天气信息更新,它就会及时向你发送最新的天气信息。

情景2:
杂志的订阅,你只需向邮局订阅杂志,缴纳一定的费用,当有新的杂志时,邮局会自动将杂志送至你预留的地址。

观察上面两个情景,有一个共同点,就是我们无需每时每刻关注我们感兴趣的东西,我们只需做的就是订阅感兴趣的事物,比如天气预报服务,杂志等,一旦我们订阅的事物发生变化,比如有新的天气预报信息,新的杂志等,被订阅的事物就会即时通知到订阅者,即我们。而这些被订阅的事物可以拥有多个订阅者,也就是一对多的关系。当然,严格意义上讲,这个一对多可以包含一对一,因为一对一是一对多的特例

然后我们看一下观察者模式的几个重要组成。

  • 观察者,我们称它为Observer,有时候我们也称它为订阅者,即Subscriber
  • 被观察者,我们称它为Observable,即可以被观察的东西,有时候还会称之为主题,即Subject

其实java中提供了Observable类和Observer接口供我们快速的实现该模式。但是为了加深印象,我们先不使用这两个类

自己实现

场景1中我们感兴趣的事情是天气预报,于是

我们应该定义一个Weather实体类。

public class Weather {

    private String description;
    
    public Weather(String description) {
        this.description = description;
    }
    
    public String getDescription() {
        return description;
    }
    
    public void setDescription(String description) {
        this.description = description;
    }
    
    @Override
    public String toString() {
        return "Weather{" +
                "description='" + description + '\'' +
                '}';
    }
}

然后定义我们的被观察者,我们想要这个被观察者能够通用,将其定义成泛型。内部应该暴露register和unregister方法供观察者订阅和取消订阅,至于观察者的保存,直接用ArrayList即可,此外,当有主题内容发送改变时,会即时通知观察者做出反应,因此应该暴露一个notifyObservers方法,以上方法的具体实现见如下代码。

public class Observable<T> {

    List<Observer<T>> mObservers = new ArrayList<Observer<T>>();
    
    public void register(Observer<T> observer) {
        if (observer == null) {
            throw new NullPointerException("observer == null");
        }
        synchronized (this) {
            if (!mObservers.contains(observer))
                mObservers.add(observer);
        }
    }
    
    public synchronized void unregister(Observer<T> observer) {
        mObservers.remove(observer);
    }
    
    public void notifyObservers(T data) {
        for (Observer<T> observer : mObservers) {
            observer.onUpdate(this, data);
        }
    }
}

而我们的观察者,只需要实现一个观察者的接口Observer,该接口也是泛型的。其定义如下。

public interface Observer<T> {
 	void onUpdate(Observable<T> observable,T data);
} 

一旦订阅的主题发送变换就会回调该接口。

我们来使用一下,我们定义了一个天气变换的主题,也就是被观察者,还有两个观察者观察天气变换,一旦变换了,就打印出天气信息,注意一定要调用被观察者的register进行注册,否则会收不到变换信息。而一旦不敢兴趣了,直接调用unregister方法进行取消注册即可

public class Main {

    public static void main(String [] args){
    
        Observable<Weather> observable=new Observable<Weather>();
        
        Observer<Weather> observer1=new Observer<Weather>() {
            @Override
            public void onUpdate(Observable<Weather> observable, Weather data) {
                System.out.println("观察者1:"+data.toString());
            }
        };
        
        Observer<Weather> observer2=new Observer<Weather>() {
            @Override
            public void onUpdate(Observable<Weather> observable, Weather data) {
                System.out.println("观察者2:"+data.toString());
            }
        };
        
        observable.register(observer1);
        observable.register(observer2);
        
        Weather weather=new Weather("晴转多云");
        observable.notifyObservers(weather);
        
        Weather weather1=new Weather("多云转阴");
        observable.notifyObservers(weather1);
        observable.unregister(observer1);
        
        Weather weather2=new Weather("台风");
        observable.notifyObservers(weather2);
    }
}

java.util包中的实现

首先直接上示例代码

public class Weather extends Observable{

    private String description;
    
    public Weather(String description) {
        this.description = description;
    }
    
    public String getDescription() {
        return description;
    }
    
    public void setDescription(String description) {
        this.description = description;
    }
    
    @Override
    public String toString() {
        return "Weather{" +
                "description='" + description + '\'' +
                '}';
    }
}
public class People implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println(String arg);
    }
}
public class Main {

    public static void main(String [] args){
    	//被观察者
        Weather weather=new Weather("晴转多云");
        //观察者
        People people = new People();

        weather.addObserver(people);
        weather.notifyObservers(weather.toString());
    }
}

工厂模式

将创建对象的具体过程隔离开

public class Factory {
    public static Object create(Class cls){
        return Class.forName(cls.getClassName).newInstance();
    }
}

建造者模式

将复杂创建过程封装在内部,对于外部调用的人来说,只需要传入建造者和建造工具,对于内部是如何建造成成品的,调用者无需关心,也不知道,具有良好的封装性,可以使客户端不用知道产品内部组成的细节。.建造者独立,容易扩展。就是解耦

public class A {
    private final String mX1;
    private int mX2;

    private A(Builder builder) {
        mX1 = builder.mX1;
        mX2 = builder.mX2;
    }

    public static final class Builder {
        private final String mX1;
        private int mX2;

        public Builder() {
        }

        public Builder setX1(String val) {
            mX1 = val;
            return this;
        }

        public Builder setX2(int val) {
            mX2 = val;
            return this;
        }

        public A build() {
            return new A(this);
        }
    }
}


//使用
new A.Builder()
      .setX1()
      .setX2()
      .build();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值