不得不会的23种Java设计模式(一)——单例模式
不得不会的23种设计模式(一)——单例模式
为什么要学习设计模式
设计模式遵循的原则
单一职责原则
开放封闭原则
依赖倒转原则
里氏代换原则
迪米特法则
设计模式的分类
单例模式
适用场景
案例
单例模式的分类
饿汉式
懒汉式
双重检测锁
静态内部类
枚举方式
为什么要学习设计模式
想一想为什么要学习数据结构和算法,就想清楚了为什么要学习设计模式了
软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
设计模式遵循的原则
单一职责原则
就一个类而言,应该仅有一个引起它变化的原因。
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受意想不到的破坏。
开放封闭原则
软件实体(类、模块、函数等等)应该可以扩展,但是不可以修改。
这个原则其实有两个特征,一个是“对于扩展时开放的”,另一个是“对于更改时封闭的”。
依赖倒转原则
抽象不应该依赖细节,细节应该依赖于抽闲。
依赖倒转原则:
A. 高层模块不应该依赖底层模块。两个都应该依赖抽象。
B. 抽象不应该依赖细节。细节应该依赖抽象。
里氏代换原则
子类必须能够代替它们的父类型。
因为有了这个原则,使得继承复用成为了可能,只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也能够在父类的基础上添加新的行为。
迪米特法则
如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
1.迪米特法则首先强调的前提是在类的结构设计上,每一个类都应当尽量降低成员的访问权限,也就是说,一个类包装好自己的private状态,不需要让别的类知道的字段或行为就不要公开。
2.类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。也就是说,信息的隐藏促进了软件的复用。
设计模式的分类
创建型模式:对象实例化的模式,创建型模式用于解耦对象的实例化过程。
结构型模式:把类或对象结合在一起形成一个更大的结构。
行为型模式:类和对象如何交互,及划分责任和算法。
单例模式
保证在一个JVM中,该对象只有一个实例存在
适用场景
某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
有些类比如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。
案例
package test19;
public class Singleton {
/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
private static Singleton instance = null;
/* 私有构造方法,防止被实例化 */
private Singleton() {
}
/* 静态方法,创建实例 */
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
package test19;
public class Main {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
Singleton singleton1 = Singleton.getInstance();
System.out.println(singleton == singleton1);
}
}
单例模式的分类
饿汉式
类初始化时创建单例,线程安全,适用于单例占内存小的场景。
package test19;
public class Singleton {
private static Singleton instance = new Singleton();
/* 私有构造方法,防止被实例化 */
private Singleton() {
}
/* 静态方法,创建实例 */
public static Singleton getInstance() {
return instance;
}
}
懒汉式
需要创建单例实例的时候再创建,需要考虑线程安全(性能不太好)
package test19;
public class Singleton {
private static Singleton instance = null;
/* 私有构造方法,防止被实例化 */
private Singleton() {
}
/* 静态方法,创建实例 */
public static synchronized Singleton getInstance() {
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
双重检测锁
效率高(假如两个线程A和B,A执行了if(instance == null)语句,它会认为单例对象没有创建,此时线程切到B也执行了同样的语句,B也认为单例对象没有创建,然后两个对象依次执行同步代码块)
package test19;
public class Singleton {
private static volatile Singleton instance = null;
/* 私有构造方法,防止被实例化 */
private Singleton() {
}
/* 静态方法,创建实例 */
public static Singleton getInstance() {
// 创建单例对象成功之后,直接跳过该段逻辑
if(null == instance){
synchronized (Singleton.class){
if(null == instance){
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类
同时保证延迟加载和线程安全
package test19;
public class Singleton {
//静态内部类
private static class SingletonHolder{
public static Singleton instance = new Singleton();
}
/* 私有构造方法,防止被实例化 */
private Singleton() {
}
/* 静态方法,创建实例 */
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
枚举方式
除了线程安全和防止反射调用构造器,还提供了自动序列化机制,防止反序列化的时候创建新的对象。
package test19;
public enum Singleton {
instance;
}
package test19;
public class Main {
public static void main(String[] args) {
Singleton singleton = Singleton.instance;
Singleton singleton1 = Singleton.instance;
System.out.println(singleton == singleton1);
}
}