1.观察者模式
观察者模式(Observer Pattern)是一种常用的设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象发生变化时,它的所有依赖者都会得到通知并且自动更新。
在观察者模式中,有两个重要的角色,即主题(Subject)和观察者(Observer)。主题维护了一个观察者列表,并且提供了注册和删除观察者的接口,以及通知观察者的接口。观察者则定义了接收主题通知的方法,以便主题在状态发生改变时能够通知观察者。
使用观察者模式的好处在于,它可以让对象之间的耦合度降低,使得对象之间的交互更加灵活。如果我们需要增加一个新的观察者,只需要让其实现 Observer 接口,并将其注册到主题中即可,不需要修改原有的代码。
观察者模式在实际开发中广泛应用,例如 Java 中的事件监听机制、Android 中的广播机制等等。
示例
下面是一个以新闻为主题的观察者模式实例。 |
创建被观察者接口 NewsPublisher,定义 register、unregister 和 notifyObservers 方法。
public interface NewsPublisher {
void register(NewsSubscriber subscriber);
void unregister(NewsSubscriber subscriber);
void notifyObservers(String news);
}
创建观察者接口 NewsSubscriber,定义 update 方法。
public interface NewsSubscriber {
void update(String news);
}
创建一个新闻源类 Newspaper 实现 NewsPublisher 接口,用于发布新闻。
import java.util.ArrayList;
import java.util.List;
public class Newspaper implements NewsPublisher {
private List<NewsSubscriber> subscribers = new ArrayList<>();
public void addNews(String news) {
System.out.println("Newspaper: " + news);
notifyObservers(news);
}
@Override
public void register(NewsSubscriber subscriber) {
subscribers.add(subscriber);
}
@Override
public void unregister(NewsSubscriber subscriber) {
subscribers.remove(subscriber);
}
@Override
public void notifyObservers(String news) {
for (NewsSubscriber subscriber : subscribers) {
subscriber.update(news);
}
}
}
创建多个观察者类 TVNewsSubscriber、WebNewsSubscriber 实现 NewsSubscriber 接口,用于接收新闻。
public class TVNewsSubscriber implements NewsSubscriber {
private String name;
public TVNewsSubscriber(String name) {
this.name = name;
}
@Override
public void update(String news) {
System.out.println("TV News Subscriber " + name + ": " + news);
}
}
public class WebNewsSubscriber implements NewsSubscriber {
private String name;
public WebNewsSubscriber(String name) {
this.name = name;
}
@Override
public void update(String news) {
System.out.println("Web News Subscriber " + name + ": " + news);
}
}
在客户端程序中,实例化 Newspaper 并向其中添加订阅者,然后调用 addNews 方法发布新闻。
package org.example.observer;
public class Test {
public static void main(String[] args) {
Newspaper newspaper = new Newspaper();
newspaper.register( new WebNewsSubscriber("张三"));
newspaper.register(new WebNewsSubscriber("李四"));
newspaper.register(new WebNewsSubscriber("王五"));
newspaper.addNews("1111");
}
}
应用场景
Java的观察者模式主要应用在以下场景: |
生活实例
观察者模式是一种设计模式,其目的是在对象之间建立一种一对多的依赖关系,以便当一个对象的状态发生变化时,所有依赖于它的对象都能够得到通知并自动更新。以下是观察者模式在生活中的一些例子: |
2.单例模式
单例模式是一种常见的创建型设计模式,用于限制一个类只能创建一个对象实例。在单例模式中,一个类只有一个对象实例存在,并且该对象可以被系统中的所有其他对象所访问。
单例模式通常会在需要控制资源的情况下使用,例如在创建数据库连接、线程池或缓存对象等情况下。在这些场景下,单例模式可以确保系统中只有一个对象实例存在,从而避免了资源的浪费和冲突。
单例模式可以使用多种不同的实现方式,包括懒汉式、饿汉式、双重检查锁定、静态内部类、枚举等。每种实现方式都有其优缺点,需要根据具体的需求场景来选择合适的实现方式。
1.饿汉式单例模式
饿汉式单例模式是最简单的一种单例模式,实现简单,线程安全。在类加载的时候就完成了实例化,因此在调用时速度非常快,但是无法实现懒加载。
类加载时就创建了一个私有的、不可变的 Singleton 实例,任何时候都只会有一个实例存在,不会存在并发的情况,所以不会存在线程安全的问题。同时,由于 instance 被声明为 final,所以在获取 instance 时不会存在重排序的问题,保证了线程安全性。
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
2.懒汉式单例模式(线程不安全)
懒汉式单例模式在调用 getInstance() 方法时才进行实例化,实现了懒加载,但是在多线程环境下是线程不安全的。
这个单例模式在多线程环境下会不安全,因为它没有考虑线程安全,当多个线程同时调用 getInstance() 方法时,可能会导致创建出多个实例。
例如,线程 A 和线程 B 同时调用 getInstance() 方法,此时 instance 为 null,A 执行到 instance = new Singleton(); 时,由于还没有完成初始化,线程 B 也会进入到 instance == null 的判断中,并且也会创建一个新的实例。这样,就会导致多个实例的创建。
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
为了解决懒汉式单例模式的线程不安全问题,可以通过在 getInstance() 方法中加入 synchronized 关键字来实现线程安全。但是这种方法效率低下,因为每次获取实例时都需要获取锁。
这段代码是线程安全的,因为在getInstance()方法上使用了synchronized关键字,该关键字可以保证在同一时刻只有一个线程可以访问这个方法。如果在多线程环境下有多个线程同时调用getInstance()方法,由于synchronized关键字的作用,只有一个线程能够执行instance = new Singleton()这一行代码,保证了只有一个实例被创建。
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3.双重检查锁定单例模式
双重检查锁定单例模式通过加锁和双重判断的方式实现了懒加载和线程安全。
这段代码使用了“双重检查锁”机制,即在第一次检查对象实例是否存在时,如果不存在才会获得锁并创建实例。因为同步块使用了类对象Singleton.class作为锁对象,所以在同步块中可以保证只有一个线程进入并创建实例。同时使用了volatile关键字来确保多线程环境下的可见性,即一个线程修改了instance的值,其他线程能够立刻看到这个值的变化,从而避免出现脏读的情况。因此,这段代码是线程安全的。
|
4.静态内部类单例模式
这种方式是使用了类的静态内部类来实现单例模式,具有线程安全的优点。类的静态内部类在类加载时只会被加载一次,同时静态内部类不会在单例类加载时被初始化,只有在调用静态内部类中的方法时才会被初始化,而静态初始化只会执行一次,因此能够保证线程安全。此外,由于 SingletonHolder 是私有静态内部类,因此外部无法访问,也保证了单例的唯一性。
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
5.枚举单例模式
枚举单例模式是一种更加简洁的实现方式,枚举常量只会被初始化一次,因此可以实现单例模式。
这个实现方式是使用了 Java 的枚举类型来实现单例模式,而 Java 的枚举类型保证了在任何情况下都只会有一个实例。因此这种实现方式是线程安全的。枚举类型的实现方式是由 JVM 内部保证线程安全的。在 Java 中,枚举类型是天然的单例模式,且枚举类型在多线程环境下也是线程安全的。
public enum Singleton {
INSTANCE;
public Singleton2 getInstance() {
return Singleton2.getInstance();
}
}
说明
1. 私有构造函数`Singleton()`:防止外部直接实例化该类,仅允许通过`getInstance()`方法获取该类的唯一实例。 |
应用场景
Java单例模式通常在以下场景中使用: |
生活实例
Plaintext |