一、设计模式分类
• 创建型模式:
– 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。
• 结构型模式:
– 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模 式。
• 行为型模式:
– 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模 式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。
二、创建型模式
1、单例模式
核心作用:
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
实现方式:
常见五种单例模式的分类
- 懒汉式(线程安全,调用率高,但是,不能延时加载)
- 饿汉式(线程安全,调用率不高,但是,可以延时加载)
- 双重检测锁式(由于JVM底层内部模型原因,偶尔会出现问题,不建议使用)
- 静态内部类式(线程安全,调用率高,但是,可以延时加载)
- 枚举单例(线程安全,调用率高,不能延时加载)
①饿汉式
/**
* 测试饿汉式单例
*/
public class SingletonDemo1 {
// 类初始化时,立即加载这个对象,天然线程安全的
private static final SingletonDemo1 instance = new SingletonDemo1();
private SingletonDemo1(){
}
// 方法没有同步,调用效率高
public static SingletonDemo1 getInstance(){
return instance;
}
}
②懒汉式
/**
* 测试懒汉式单例
*/
public class SingletonDemo2 {
// 类初始化时,不初始化这个对象(延时加载,真正用到时候再创建)
private static SingletonDemo2 instance;
private SingletonDemo2() {
}
// 方法同步,调用率低
public static synchronized SingletonDemo2 getInstance(){
if(instance == null){
instance = new SingletonDemo2();
}
return instance;
}
}
③双重检测锁
/**
* 双重检测锁实现单例模式
*
* 这个模式将同步内容下方到if内部,提高了执行的效率 不必每次获取对象时都进行同步,只有第一次才同步 创建了以后就没必要了。
*
* 由于编译器优化原因和JVM底层内部模型原因, 偶尔会出问题。不建议使用。
*/
public class SingletonDemo3 {
private static SingletonDemo3 instance = null;
private SingletonDemo3() {
}
public static SingletonDemo3 getInstance() {
if(instance == null) {
SingletonDemo3 sc;
synchronized (SingletonDemo3.class) {
sc = instance;
if(sc == null) {
synchronized (SingletonDemo3.class) {
if (sc == null) {
sc = new SingletonDemo3();
}
}
instance = sc;
}
}
}
return instance;
}
}
④静态内部类
/**
* 静态内部类实现方式(也是一种懒加载方式)
*
* – 外部类没有static属性,则不会像饿汉式那样立即加载对象。
* – 只有真正调用getInstance(),才会加载静态内部类。加载类时是线程安全的。
* instance是static final 类型,保证了内存中只有这样一个实例存在,而
* 且只能被赋值一次,从而保证了线程安全性.
* – 兼备了并发高效调用和延迟加载的优势!
*/
public class SingletonDemo4 {
private SingletonDemo4(){
}
private static class SingletonClassInstance{
private static final SingletonDemo4 instance = new SingletonDemo4();
}
private static SingletonDemo4 getInstance(){
return SingletonClassInstance.instance;
}
}
⑤枚举的方式
/**
* 枚举的方式实现单例模式(没有延时加载)
* 枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!
*/
public enum SingletonDemo5 {
// 这个枚举元素本身就是单例的
//定义两个实例,一个表示请求成功,一个表示请求失败
HTTP_200(200,"请求成功"), HTTP_500(500,"请求失败");
//枚举和普通的类一样,可以定义属性,构造函数,getter setter,普通方法,
private Integer code;
private String msg;
SingletonDemo5(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
public void setCode(Integer code) {
this.code = code;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
测试代码
public class Client {
public static void main(String[] args) {
SingletonDemo5 instance = SingletonDemo5.HTTP_200;
System.out.println(instance == SingletonDemo5.HTTP_200);
System.out.println(instance.getCode());
System.out.println(instance.getMsg());
}
反射与反序列化漏洞和解决方案
测试案例
import java.io.ObjectStreamException;
import java.io.Serializable;
/**
* 测试懒汉式单例
*/
public class SingletonDemo6 implements Serializable {
private static SingletonDemo6 instance;
private SingletonDemo6() {
// 1.反射漏洞解决方案
// 通过手动抛出异常,避免通过反射创建多个单例对象!
if (instance != null) {
throw new RuntimeException("只能创建一个对象");
}
}
public static synchronized SingletonDemo6 getInstance(){
if(instance == null){
instance = new SingletonDemo6();
}
return instance;
}
// 2.反序列化漏洞解决方案
//反序列化时,如果对象所在类定义了readResolve(),(实际是一种回调),定义返回哪个对象。
private Object readResolve() throws ObjectStreamException {
return instance;
}
}
测试代码
/**
* 测试反射和反序列化破解单例模式
*/
public class Client2 {
@Test
public void demoTest1() throws Exception {
SingletonDemo6 s1 = SingletonDemo6.getInstance();
SingletonDemo6 s2 = SingletonDemo6.getInstance();
// 通过反射的方式直接调用私有构造器创建对象
Class<?> cls = Class.forName("com.xcm.singleton.SingletonDemo6");
Constructor<?> constructor = cls.getDeclaredConstructor();
constructor.setAccessible(true);
SingletonDemo6 s3 = (SingletonDemo6) constructor.newInstance();
SingletonDemo6 s4 = (SingletonDemo6) constructor.newInstance();
// 通过反序列化的方式构造多个对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
oos.writeObject(s1);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
SingletonDemo6 s5 = (SingletonDemo6) ois.readObject();
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
System.out.println(s4);
System.out.println(s5);
}
}
各种单例模式效率测试
测试代码
public class Client3 {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
int threadNum = 10;
// 同步辅助类,它允许一个或者多个线程等待
final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
for(int i=0; i<threadNum; i++){
new Thread(new Runnable() {
public void run() {
for (int i=0; i<1000000; i++){
// Object instance = SingletonDemo4.getInstance();
Object instance = SingletonDemo5.HTTP_200;
}
// 计数减一
countDownLatch.countDown();
}
}).start();
}
// 阻塞当前的线程,直到计数器的值为0
countDownLatch.await();
long end = System.currentTimeMillis();
System.out.println(end-start);
}
}
结果:
– 单例对象 占用 资源 少,不需要 延时加载:
• 枚举式 好于 饿汉式
– 单例对象 占用 资源 大,需要 延时加载:
• 静态内部类式 好于 懒汉式
2、工厂模式
①简单工厂 / 静态工厂
要点:
• 用来生产同一等级结构中的任意产品(对于新增的产品,需要修改已有的代码)
• 虽然某种程度不符合设计原则,但实际使用最多。
②工厂方法
要点:
• 用来生产同一等级结构中的固定商品(支持增加任意商品)
• 扩展会增加类的数量,从而使复杂度提高
• 不修改已有类的前提下,通过增加新的工厂类实现扩展。
案例:
(1)定义一个生产汽车的接口
public interface Car {
void run();
}
(2)定义东风汽车、长安汽车的实体类
public class DongFengCar implements Car {
public void run() {
System.out.println("东风汽车");
}
}
public class ChangAnCar implements Car {
public void run() {
System.out.println("长安汽车");
}
}
(3)定义生产东风汽车、长安汽车的工厂类
public class DongFengFactory {
public static Car createCar(){
return new DongFengCar();
}
}
public class ChangAnFactory {
public static Car createCar(){
return new ChangAnCar();
}
}
(4)测试类
public class Client {
public static void main(String[] args) {
Car car1 = ChangAnFactory.createCar();
Car car2 = DongFengFactory.createCar();
}
}
③抽象工厂
要点:
• 主要生产产品族,用来一次性生产不同的产品
• 抽象工厂是工厂方法的升级版
案例
(1)定义生产发动机、座椅的接口
public interface Engine {
void run();
}
public interface Seat {
void sleep();
}
(2)实现接口,定义高端发动机、高端座椅的实体类
public class HighEngine implements Engine {
public void run() {
System.out.println("跑的快");
}
}
public class HighSeat implements Seat {
public void sleep() {
System.out.println("睡觉舒服");
}
}
(3)定义汽车的接口,汽车包含发动机与座椅
public interface CarFactory {
Engine createEngine();
Seat createSeat();
}
(4)实现汽车接口,定义高端汽车的工厂类
public class HighCarFactory implements CarFactory {
public Engine createEngine() {
return new HighEngine();
}
public Seat createSeat() {
return new HighSeat();
}
}
(5)测试
public class Client {
public static void main(String[] args) {
CarFactory factory = new HighCarFactory();
Engine engine = factory.createEngine();
engine.run();
}
}
3、构建者模式
本质
– 分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。 从而可以构 造出复杂的对象。这个模式适用于:某个对象的构建过程复杂的情况下使用。
– 由于实现了构建和装配的解耦。不同的构建器,相同的装配,也可以做出不同的对象; 相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配 算法的解耦,实现了更好的复用
案例
(1)定义一个飞船,飞船包含发动机和逃逸塔两个零件
/**
* 飞船类
*/
public class AirShip {
private Engine engine; // 发动机
private EscapeTower escapeTower; // 逃逸塔
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public EscapeTower getEscapeTower() {
return escapeTower;
}
public void setEscapeTower(EscapeTower escapeTower) {
this.escapeTower = escapeTower;
}
}
class Engine{
String name;
public