1、单例模式
单例模式(Singleton)的目的是为了保证在一个进程中,某个类有且仅有一个实例。
因为这个类只有一个实例,因此,自然不能让调用方使用new Xyz()
来创建实例了。所以,单例的构造方法必须是private
,这样就防止了调用方自己创建实例,但是在类的内部,是可以用一个静态字段来引用唯一创建的实例的:
单例模式的实现方式很简单:
- 只有
private
构造方法,确保外部无法实例化; - 通过
private static
变量持有唯一实例,保证全局唯一性; - 通过
public static
方法返回此唯一实例,使外部调用方能获取到实例。
方式一:饿汉模式
在初始化的时候,就创建了唯一的实例,不管是否需要用到。不需要自己加同步,一定产生唯一的实例。
public class Singleton {
// 静态字段引用唯一实例:
private static final Singleton INSTANCE = new Singleton();
// 通过静态方法返回实例:
public static Singleton getInstance() {
return INSTANCE;
}
// private构造方法保证外部无法实例化:
private Singleton() {
}
}
方式二:懒汉模式
或者直接把static
变量暴露给外部:
在初始化类的时候,不创建唯一的实例,而是等到真正需要用到的时候才创建。必须加上同步,否则有可能依然创建多个实例。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() { //这是非线程安全的,在static后面加synchronized就是线程安全的
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
方式三:双重检验
因为不加synchronized会导致多线程环境创建多个对象,但是如果加了线程锁,会降低系统性能,所以先在前面加个判断,两者折衷。
public 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;
}
}
Java标准库有一些类就是单例,例如Runtime
这个类:
Runtime runtime = Runtime.getRuntime();
2、观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
观察者模式(Observer)又称发布-订阅模式(Publish-Subscribe:Pub/Sub)。它是一种通知机制,让发送通知的一方(被观察方)和接收通知的一方(观察者)能彼此分离,互不影响。
public class Store {
private List<ProductObserver> observers = new ArrayList<>();
private Map<String, Product> products = new HashMap<>();
// 注册观察者:
public void addObserver(ProductObserver observer) {
this.observers.add(observer);
}
// 取消注册:
public void removeObserver(ProductObserver observer) {
this.observers.remove(observer);
}
public void addNewProduct(String name, double price) {
Product p = new Product(name, price);
products.put(p.getName(), p);
// 通知观察者:
observers.forEach(o -> o.onPublished(p));
}
public void setProductPrice(String name, double price) {
Product p = products.get(name);
p.setPrice(price);
// 通知观察者:
observers.forEach(o -> o.onPriceChanged(p));
}
}
观察者类型就可以无限扩充,而且,观察者的定义可以放到客户端:
// observer:
Admin a = new Admin();
Customer c = new Customer();
// store:
Store store = new Store();
// 注册观察者:
store.addObserver(a);
store.addObserver(c);
3、装饰者模式
动态地给一个对象添加一些额外的职责。就增加功能来说,相比生成子类更为灵活。
装饰器(Decorator)模式,是一种在运行期动态给某个对象的实例增加功能的方法。
public interface TextNode {
// 设置text:
void setText(String text);
// 获取text:
String getText();
}
对于核心节点,例如<span>
,它需要从TextNode
直接继承:
public class SpanNode implements TextNode {
private String text;
public void setText(String text) {
this.text = text;
}
public String getText() {
return "<span>" + text + "</span>";
}
}
紧接着,为了实现Decorator模式,需要有一个抽象的Decorator类:
public abstract class NodeDecorator implements TextNode {
protected final TextNode target;
protected NodeDecorator(TextNode target) {
this.target = target;
}
public void setText(String text) {
this.target.setText(text);
}
}
这个NodeDecorator
类的核心是持有一个TextNode
,即将要把功能附加到的TextNode
实例。接下来就可以写一个加粗功能:
public class BoldDecorator extends NodeDecorator {
public BoldDecorator(TextNode target) {
super(target);
}
public String getText() {
return "<b>" + target.getText() + "</b>";
}
}
类似的,可以继续加ItalicDecorator
、UnderlineDecorator
等。客户端可以自由组合这些Decorator:
TextNode n1 = new SpanNode();
TextNode n2 = new BoldDecorator(new UnderlineDecorator(new SpanNode()));
TextNode n3 = new ItalicDecorator(new BoldDecorator(new SpanNode()));
n1.setText("Hello");
n2.setText("Decorated");
n3.setText("World");
System.out.println(n1.getText());
// 输出<span>Hello</span>
System.out.println(n2.getText());
// 输出<b><u><span>Decorated</span></u></b>
System.out.println(n3.getText());
// 输出<i><b><span>World</span></b></i>
4、代理模式
为其他对象提供一种代理以控制对这个对象的访问。
4.1 静态代理
创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。
接口:
public interface HelloInterface {
void sayHello();
}
被代理类:
public class Hello implements HelloInterface{
@Override
public void sayHello() {
System.out.println("Hello zhanghao!");
}
}
代理类:
public class HelloProxy implements HelloInterface{
private HelloInterface helloInterface = new Hello();
@Override
public void sayHello() {
System.out.println("Before invoke sayHello" );
helloInterface.sayHello();
System.out.println("After invoke sayHello");
}
}
代理类调用:
被代理类被传递给了代理类HelloProxy,代理类在执行具体方法时通过所持用的被代理类完成调用。
public static void main(String[] args) {
HelloProxy helloProxy = new HelloProxy();
helloProxy.sayHello();
}
输出:
Before invoke sayHello
Hello zhanghao!
After invoke sayHello
使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。
4.2 动态代理
①定义接口
public interface HelloImplements {
void sayHello();
}
②实现接口(作为被代理类)
public class Hello implements HelloImplements {
@Override
public void sayHello() {
System.out.println("被代理对象打印");
}
}
③动态代理类
public class ProxyHello implements InvocationHandler {
private Object delegate;
public ProxyHello(Object object){
this.delegate = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("动态代理先打印");
method.invoke(delegate,args);
System.out.println("动态代理打印结束");
return null;
}
}
④测试类中调用
public class TestProxyHello {
public static void main(String[] args) {
IHello hello = new Hello();
InvocationHandler proxyHello = new ProxyHello(hello);
IHello iHello = (IHello) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), proxyHello);
iHello.sayHello();
}
}
5、工厂方法
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
工厂方法即Factory Method,是一种对象创建型模式。
工厂方法的目的是使得创建对象和使用对象是分离的,并且客户端总是引用抽象工厂和抽象产品:
通常我们会在接口Factory
中定义一个静态方法getFactory()
来返回真正的子类:
public interface NumberFactory {
// 创建方法:
Number parse(String s);
// 获取工厂实例:
static NumberFactory getFactory() {
return impl;
}
static NumberFactory impl = new NumberFactoryImpl();//注意这里返回的已经是接口的实现类的对象
}
在客户端中,我们只需要和工厂接口NumberFactory
以及抽象产品Number
打交道:
NumberFactory factory = NumberFactory.getFactory();
Number result = factory.parse("123.456");
调用方可以完全忽略真正的工厂NumberFactoryImpl
和实际的产品BigDecimal
,这样做的好处是允许创建产品的代码独立地变换,而不会影响到调用方。
有的童鞋会问:一个简单的parse()
需要写这么复杂的工厂吗?实际上大多数情况下我们并不需要抽象工厂,而是通过静态方法直接返回产品,即:
public class NumberFactory {
public static Number parse(String s) {
return new BigDecimal(s);
}
}
6、抽象工厂
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
抽象工厂模式(Abstract Factory)是一个比较复杂的创建型模式。
抽象工厂模式和工厂方法不太一样,它要解决的问题比较复杂,不但工厂是抽象的,产品是抽象的,而且有多个产品需要创建,因此,这个抽象工厂会对应到多个实际工厂,每个实际工厂负责创建多个实际产品:
7、生产者-消费者模式
生产者-消费者模式,生产者生产商品,然后通知消费者进行消费。
①定义生产者
public class Vendor {
// 定义库存数量
private int count;
// 定义最大库存
private final int MAX_COUNT = 10;
public synchronized void production() {
while (count >= MAX_COUNT) {
try {
System.out.println(Thread.currentThread().getName() + "库存数量达到最大值,停止生产。");
// 此时生产线程全部进入等待状态
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 否则生产商品
count++;
System.out.println(Thread.currentThread().getName() + "正在生产商品,当前库存为:" + count);
notifyAll();
}
public synchronized void consumers() {
while (count <= 0) {
try {
System.out.println(Thread.currentThread().getName() + "没有商品了,消费者处于等待状态...");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
System.out.println(Thread.currentThread().getName() + "正在消费,当前库存为:" + count);
notifyAll();
}
}
②分别定义两条线程。
public class SetTarget implements Runnable {
private Vendor vendor;
public SetTarget(Vendor vendor) {
this.vendor = vendor;
}
@Override
public void run() {
while(true){
vendor.production();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class GetTarget implements Runnable {
private Vendor vendor;
public GetTarget(Vendor vendor) {
this.vendor = vendor;
}
@Override
public void run() {
while(true){
vendor.consumers();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
③主方法。
public class Demo {
public static void main(String[] args) {
Vendor vendor = new Vendor();
SetTarget set = new SetTarget(vendor);
GetTarget get = new GetTarget(vendor);
// 开启线程生产商品
new Thread(set).start();
new Thread(set).start();
new Thread(set).start();
new Thread(set).start();
// 开启消费者线程
new Thread(get).start();
}
}