今天也是认真摸鱼的一天~
一、设计模式
Abstract Factory | 抽象工厂模式 | Builder | 建造者模式 | Observer | 观察者模式 |
Factory Method | 工厂方法模式 | Prototype | 原始模型模式 | State | 状态模式 |
Singleton | 单例模式 | Facade | 门面模式 | Strategy | 策略模式 |
Adapter | 适配器模式 | Bridge | 桥梁模式 | Template Method | 模板方法模式 |
Composite | 合成模式 | Decorator | 装饰模式 | Chain Of Responsibility | 责任链模式 |
Flyweight | 享元模式 | Proxy | 代理模式 | Memento | 备忘录模式 |
Command | 命令模式 | Interpreter | 解释器模式 | Mediator | 调停者模式 |
Visitor | 访问者模式 | Iterator | 迭代子模式 |
以上是23种设计模式
不能再多了
接下来让我们详细研究一下单例模式吧!
为什么要研究呢??
因为考试要考,划重点!!
二、单例模式
那么问题来了,什么是单例模式?
1、定义
单例模式是在整个应用中保证只有一个类的实例存在。
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。
2、具体实现
2.1 饿汉式--静态常量[可用]
public class Singleton {
private static final Singleton instance = new Singleton();;
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
缺点:在类装载的时候就完成实例化,没有达到Lazy Loading(懒加载延迟加载)的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
2.2 饿汉式--静态代码块[可用]
public class Singleton {
private static Singleton instance;
static {
instance = new Singleton();
}
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
优缺点和上面是一样的。
这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。
2.3 懒汉式(线程不安全)[不可用]
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if(instance== null){
instance= new Singleton();
}
return instance;
}
}
优点:可以在使用时开辟空间,不使用不开辟空间。
缺点:线程不安全
这种写法起到了Lazy Loading的效果,但是只能在单线程下使用。
如果在多线程下,一个线程进入了if (instance== null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。
所以在多线程环境下不可使用这种方式。
2.4 懒汉式(线程安全,同步方法)[不推荐使用]
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance== null) {
instance= new Singleton();
}
return instance;
}
}
优点:懒汉式,用的时候开辟内存。线程安全。
缺点:效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低要改进。
解决上面第三种实现方式的线程不安全问题,做个线程同步就可以了,于是就对getInstance()方法进行了线程同步。
2.5 懒汉式(线程安全--同步代码块,双重检查)[推荐用]
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance== null) {
synchronized (Singleton.class) {
if (instance== null) {
instance= new Singleton();
}
}
}
return instance;
}
}
优点:线程安全;延迟加载;效率较高。
Double-Check概念对于多线程开发者来说不会陌生,如代码中所示,我们进行了两次if (instance== null)检查,这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断if (instance== null),直接return实例化对象。
换句话说:
假设有两个线程--线程1和线程2。
当线程1执行到同步代码块时,进行同步锁,准备执行判断条件时,cpu时间到了,它无情的走了,就剩线程1一个孤孤单单没有完成任务。
这时候轮到线程2开始任务,当线程2走到同步代码块时,发现它被锁住了,这个时候线程2 只能等线程1完成任务之后再去做。
当线程1完成任务后,线程2再判断if (instance== null)条件的时候,发现不满足条件了
2.6 静态内部类[推荐用]
public class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
优点:避免了线程不安全,延迟加载,效率高。
这种方式跟饿汉式方式采用的机制类似,但又有不同。
两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
三、工厂方法模式
工厂方法模式是一个抽象产品类派生出多个具体产品类;一个抽象工厂类派生出多个具体工厂类;每个具体工厂类只能创建一个具体产品类的实例。
定义一个创建对象的接口(即抽象工厂类),让其子类(具体工厂类) 决定实例化哪一个类(具体产品类)。“一对一”的关系。
抽象工厂角色(Creator):是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。
具体工厂角色(Concrete Creator):这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。在上图中有两个这样的角色:BulbCreator与TubeCreator。
抽象产品角色(Product):工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。在上图中,这个角色是Light。
具体产品角色(Concrete Product):这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。
四、代理模式
代理模式是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
生活中的例子:过年加班比较忙,没空去买火车票,这时可以打个电话到附近的票务中心,叫他们帮你买张回家的火车票,当然这会附加额外的劳务费。但要清楚票务中心自己并不卖票,只有火车站才真正卖票,票务中心卖给你的票其实是通过火车站实现的。这点很重要!
上面这个例子,你就是“客户”,票务中心就是“代理角色”,火车站是“真实角色”,卖票称为“抽象角色”!
抽象主题角色(Subject):抽象主题类可以是抽象类,也可以是接口,是一个最普通的业务类型定义,无特殊要求。
具体主题角色(RealSubject):也叫被委托角色、被代理角色。是业务逻辑的具体执行者。
代理主题角色(Proxy):也叫委托类、代理类。它把所有抽象主题类定义的方法给具体主题角色实现,并且在具体主题角色处理完毕前后做预处理和善后工作。
五、适配器模式
适配器模式是将一个接口转换成客户希望的另外一个接口。该模式使得原本不兼容的类可以一起工作。
目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
需要适配的类(Adaptee):需要适配的类或适配者类。
适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。
六、观察者模式
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。
抽象主题角色:把所有对观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的观察者。抽象主题提供一个方法,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
具体主题角色:在具体主题内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个子类实现。
抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。
具体观察者角色:该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。
七、设计模式原则
单一职责原则
一个类只负责一个功能领域中的相应职责,原则是实现高内聚、低耦合。
开放封闭原则
一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
依赖倒置原则
抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。
接口隔离原则
使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
里氏代换原则
所有引用基类(父类)的地方必须能透明地使用其子类的对象。
迪米特法则
一个软件实体应当尽可能少地与其他实体发生相互作用。
要记的东西越来越多
没想到 Java已是最熟悉的陌生人了
可恶啊