设计模式(二)
单例模式
单例对象的类必须保证只有一个实例存在。
使用场景:
-
需要频繁的实例化然后销毁的对象
-
创建对象时耗时过多或者消耗资源过多,但又经常用的的对象。
-
有状态的工具类对象。
-
频繁访问数据库或文件的对象。
有状态:就是有储存数据的功能,有状态对象,就是有实例变量的对象,可以保存数据,非线程安全
无状态:就是一次操作,不保存数据,无状态对象,就是没有实例变量的对象,不保存数据,不变类,线程安全
常见写法:
-
恶汉式
public class Singleton { private static final Singleton singleton = new Singleton(); private Singleton {} public static Singleton getInstance(){ return singleton; } } -
懒汉式
public class Singleton { private static final Singleton singleton = null; private Singleton {} public static Singleton getInstance(){ if(singleton == null){ singleton = new Singleton(); } return singleton; } } -
双重检查锁
volatitle 禁止指令重排,所有的写(write)操作都将发生在读(read)操作之前。实例化对象的过程,1. 分配内存空间,2.初始化对象,3.将对象指向刚分配的内存空间,编译器为了性能原因,将2和3重排序,
public class Singleton { private volatitle static Singleton singleton = null; private Singleton {} public static Singleton getInstance(){ if(singleton == null){ //当一次实例化结束之后,直接返回实例化对象,加快效率 synchronized(Singleton.class){ if(singleton == null){ //当线程a进入实例化完成之后,线程b进入之后判断,直接退出 ,当没有禁止重排的时候可能返回一个未初始化的对象。 singleton = new Singleton(); } } } return singleton; } } -
匿名内部类
jvm会保证一个类的初始化方法会在多线程环境中正确加锁,同步,如果多个线程去初始化同一个类,只有一个线程执行初始化方法,其他线程会阻塞,注意当一个线程执行完毕初始化方法后,其他线程被唤醒也不会执行初始化方法了,一个类只会初始化一次
public class Singleton{ private Singleton {} private static class Sinleton01 { private static final Singleton singleton = new Singleton(); } public static Singleton getInstance(){ return Sinleton01.sinleton; } } -
枚举类
public enum Singleton{ INSATANCE; }
注:可以通过反射破坏单利
public class Client { public static void main(String[] args) throws Exception { Class<Singleton> clazz = (Class<Singleton>)Class.forName("com.course.design.singleton.Singleton"); Constructor<Singleton> constructor = clazz.getDeclaredConstructor(null); constructor.setAccessible(true);//跳过权限验证 Singleton singleton = constructor.newInstance(); Singleton singleton1 = constructor.newInstance(); System.out.println(singleton1); System.out.println(singleton); } }也可阻止,在私有的构造方法中加一个判断。
private Singleton(){ if (Singleton.singleton == null) { throw new RuntimeException(); } } -
命令模式
-
包含角色
-
receiver 接收者角色
干活的角色,命令传递到这里应该被执行的。
-
command 命令角色
所需的所有命令在这里声明
-
invoker 调用者角色
接收命令,并执行
-
-
代码
/** * 抽象接收者,每个具体接收者都必须完成的命令 */ public abstract class Receiver { public abstract void doSomething(); }public class ConcreteReceiver1 extends Receiver{ //每个接收者都必须处理一定的业务逻辑 public void doSomething(){ System.out.println("接收者1 : 正在执行命令。。。。。。"); } } public class ConcreteReceiver2 extends Receiver{ //每个接收者都必须处理一定的业务逻辑 public void doSomething(){ System.out.println("接收者2 : 正在执行命令。。。。。。"); } }public abstract class Command { //每个命令类都必须有一个执行命令的方法 public abstract void execute(); }public class ConcreteCommand1 extends Command { //对哪个Receiver类进行命令处理 private Receiver receiver; //构造函数传递接收者 public ConcreteCommand1(Receiver _receiver){ this.receiver = _receiver; } //必须实现一个命令 public void execute() { //业务处理 this.receiver.doSomething(); } } public class ConcreteCommand2 extends Command { //哪个Receiver类进行命令处理 private Receiver receiver; //构造函数传递接收者 public ConcreteCommand2(Receiver _receiver){ this.receiver = _receiver; } //必须实现一个命令 public void execute() { //业务处理 this.receiver.doSomething(); } }public class Invoker { private Command command; //受气包,接受命令 public void setCommand(Command _command){ this.command = _command; } //执行命令 public void action(){ this.command.execute(); } }public class Client { public static void main(String[] args) { //首先声明调用者Invoker Invoker invoker = new Invoker(); //定义接收者 Receiver receiver = new ConcreteReciver1(); //定义一个发送给接收者的命令 Command command = new ConcreteCommand1(receiver); //把命令交给调用者去执行 invoker.setCommand(command); invoker.action(); } } -
命令模式的优缺点
-
优点
- 类间解耦 :调用者角色与接收者之间没有任何依赖关系
- 可扩展性
-
缺点
- 当命令增加,导致命令类过多,臃肿
-

浙公网安备 33010602011771号