设计模式之单例模式及七大原则
设计模式。
文章目录
GoF(Gang of Four) 23。
创建型模式。5
单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。
结构性模式。7
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
行为型模式。11
模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。
OOP 七大原则。
-
开-闭原则(Open-Closed Principle, OCP)。
对扩展开放,对修改关闭。 -
里氏替换原则(Liskov Substitution Principle, LSP)。
继承必须确保超类所拥有的性质在子类中仍然成立。 -
依赖倒置原则(Dependence Inversion Principle)。
要面向接口编程,不要面向实现编程。 -
接口隔离原则(Interface Segregation Principle, ISP)。
要为各个类建立他们需要的专用接口。 -
合成/聚合复用原则(Composite/Aggregate Reuse Principle, CARP)。
尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。 -
迪米特法则(Law of Demeter, LoD)。
只与你的直接朋友交谈,不要跟“陌生人”说话。 -
单一职责原则(Simple Responsibility Principle, SRP)。
控制类的粒度大小,将对象解耦,提高其内聚性。
单例模式。
饿汉式。
package com.geek.singleton; /**
* 饿汉式单例模式。
*/ public class Hungry { // 饿汉,类一加载就创建对象。 private final static Hungry HUNGRY = new Hungry(); // 可能浪费空间。 private byte[] data1 = new byte[1024 * 1024]; private byte[] data2 = new byte[1024 * 1024]; private byte[] data3 = new byte[1024 * 1024]; private byte[] data4 = new byte[1024 * 1024]; // 构造器私有。 private Hungry() { } public static Hungry getInstance() { return HUNGRY; } }
懒汉式。
- 单线程情况 ok。
多线程失效。
package com.geek.singleton; /**
* 懒汉式单例模式。
*/ public class Lazy { private static Lazy lazy; private Lazy() { System.out.println(Thread.currentThread().getName() + " ok."); } public static Lazy getInstance() { if (lazy == null) { lazy = new Lazy(); } return lazy; } // 单线程下单例 ok。 // 多线程并发。 public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(Lazy::getInstance).start(); } } // Thread-0 ok. // Thread-6 ok. // Thread-3 ok. // Thread-2 ok. }
懒汉式 ~ 改进:双重检测锁懒汉式单例。DCL Double Check Lock 懒汉式单例。
加 volatile 是因为 new 对象不是原子性操作。
package com.geek.singleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /**
* 懒汉式单例模式。
*/ public class Lazy2Synchronized { private static volatile Lazy2Synchronized lazy; private Lazy2Synchronized() { } // 双重检测锁懒汉式单例。DCL Double Check Lock 懒汉式单例。 public static Lazy2Synchronized getInstance() { if (lazy == null) { synchronized (Lazy.class) { if (lazy == null) { lazy = new Lazy2Synchronized();// 不是一个原子性操作。 /**
* 1. 分配内存空间。
* 2. 执行构造方法,初始化对象。
* 3. 把引用指向对象。
*
* 期望 123
* 然而 132
* A 线程先分配内存空间,引用指向了对象,
* 这时 B 线程进来,会认为这个引用不为 null
* 直接 return lazy;。
* 此时 lazy 还没有完成构造,return null。
* (指令重排造成问题。)
*
* 所以要在 lazy 加上 volatile。
*/ } } } return lazy; } // 单线程下单例 ok。 // 多线程并发。 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { for (int i = 0; i < 10; i++) { new Thread(Lazy2Synchronized::getInstance).start(); } Lazy2Synchronized lazy1 = Lazy2Synchronized.getInstance();
反射破坏单例。
package com.geek.singleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /**
* 懒汉式单例模式。
*/ public class Lazy2Synchronized { private static volatile Lazy2Synchronized lazy; private Lazy2Synchronized() { } // 双重检测锁懒汉式单例。DCL Double Check Lock 懒汉式单例。 public static Lazy2Synchronized getInstance() { if (lazy == null) { synchronized (Lazy.class) { if (lazy == null) { lazy = new Lazy2Synchronized();// 不是一个原子性操作。 /**
* 1. 分配内存空间。
* 2. 执行构造方法,初始化对象。
* 3. 把引用指向对象。
*
* 期望 123
* 然而 132
* A 线程先分配内存空间,引用指向了对象,
* 这时 B 线程进来,会认为这个引用不为 null
* 直接 return lazy;。
* 此时 lazy 还没有完成构造,return null。
* (指令重排造成问题。)
*
* 所以要在 lazy 加上 volatile。
*/ } } } System.out.println("lazy = " + lazy); return lazy; } // 单线程下单例 ok。 // 多线程并发。 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { for (int i = 0; i < 10; i++) { new Thread(Lazy2Synchronized::getInstance).start(); // lazy = com.geek.singleton.Lazy2Synchronized@346827f //lazy = com.geek.singleton.Lazy2Synchronized@346827f //lazy = com.geek.singleton.Lazy2Synchronized@346827f } Lazy2Synchronized lazy1 = Lazy2Synchronized.getInstance(); // 反射破坏单例。 Constructor<Lazy2Synchronized> declaredConstructor = Lazy2Synchronized.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); Lazy2Synchronized lazy2 = declaredConstructor.newInstance(); System.out.println(lazy1); System.out.println(lazy2); // com.geek.singleton.Lazy2Synchronized@346827f //com.geek.singleton.Lazy2Synchronized@41629346 } // Thread-0 ok. }
解决反射。
package com.geek.singleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /**
* 懒汉式单例模式。
*/ public class Lazy2Synchronized { private static volatile Lazy2Synchronized lazy; private Lazy2Synchronized() { synchronized (Lazy2Synchronized.class) { if (lazy != null) { throw new RuntimeException("不要试图用反射破坏单例。"); } } System.out.println(Thread.currentThread().getName() + " ok."); } // 双重检测锁懒汉式单例。DCL Double Check Lock 懒汉式单例。 public static Lazy2Synchronized getInstance() { if (lazy == null) { synchronized (Lazy.class) { if (lazy == null) { lazy = new Lazy2Synchronized();// 不是一个原子性操作。 /**
* 1. 分配内存空间。
* 2. 执行构造方法,初始化对象。
* 3. 把引用指向对象。
*
* 期望 123
* 然而 132
* A 线程先分配内存空间,引用指向了对象,
* 这时 B 线程进来,会认为这个引用不为 null
* 直接 return lazy;。
* 此时 lazy 还没有完成构造,return null。
* (指令重排造成问题。)
*
* 所以要在 lazy 加上 volatile。
*/ } } } System.out.println("lazy = " + lazy); return lazy; } // 单线程下单例 ok。 // 多线程并发。 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { for (int i = 0; i < 10; i++) { new Thread(Lazy2Synchronized::getInstance).start(); // lazy = com.geek.singleton.Lazy2Synchronized@346827f //lazy = com.geek.singleton.Lazy2Synchronized@346827f //lazy = com.geek.singleton.Lazy2Synchronized@346827f } Lazy2Synchronized lazy1 = Lazy2Synchronized.getInstance(); // 反射破坏单例。 Constructor<Lazy2Synchronized> declaredConstructor = Lazy2Synchronized.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); Lazy2Synchronized lazy2 = declaredConstructor.newInstance(); System.out.println(lazy1); System.out.println(lazy2); // com.geek.singleton.Lazy2Synchronized@346827f //com.geek.singleton.Lazy2Synchronized@41629346 // 解决: // 在构造方法中加 synchronized 锁。 } // Thread-0 ok. }
破坏反射 2。
/**
* 懒汉式单例模式。
*/ public class Lazy2Synchronized { private static volatile Lazy2Synchronized lazy; private static boolean geek = true;// 使用加密。 private Lazy2Synchronized() { synchronized (Lazy2Synchronized.class) { // if (lazy != null) { // throw new RuntimeException("不要试图用反射破坏单例。"); // } // 方法 2。 if (geek == true) { geek = false; } else { throw new RuntimeException("不要试图用反射破坏单例。"); } } System.out.println(Thread.currentThread().getName() + " ok."); }
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException { for (int i = 0; i < 10; i++) { new Thread(Lazy2Synchronized::getInstance).start(); // lazy = com.geek.singleton.Lazy2Synchronized@346827f //lazy = com.geek.singleton.Lazy2Synchronized@346827f //lazy = com.geek.singleton.Lazy2Synchronized@346827f } Lazy2Synchronized lazy1 = Lazy2Synchronized.getInstance(); // 反射破坏单例。 // ~ ~ ~ 2 Field geek = Lazy2Synchronized.class.getDeclaredField("geek"); geek.setAccessible(true); // ~ ~ ~ Constructor<Lazy2Synchronized> declaredConstructor = Lazy2Synchronized.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); // ~ ~ ~ geek.set(lazy, false); // ~ ~ ~ Lazy2Synchronized lazy2 = declaredConstructor.newInstance(); System.out.println(lazy1); System.out.println(lazy2); // com.geek.singleton.Lazy2Synchronized@22673956 //com.geek.singleton.Lazy2Synchronized@41629346 }
解决反射 2。枚举。
package com.geek.singleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; // enum 本身也是一个类 class。 public enum EnumSingle { INSTANCE; public EnumSingle getInstance() { return INSTANCE; } } class Test { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { EnumSingle instance1 = EnumSingle.INSTANCE; // Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null); // Exception in thread "main" java.lang.NoSuchMethodException: com.geek.singleton.EnumSingle.<init>() // IDEA 骗了我,EnumSingle 没有空参构造。骗子 +1。 Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class); // Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects // at java.lang.reflect.Constructor.newInstance(Constructor.java:417) // 想要的异常。不能反射创建枚举。 declaredConstructor.setAccessible(true); EnumSingle instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); // EnumSingle instance2 = EnumSingle.INSTANCE; // // System.out.println("instance1 = " + instance1);// INSTANCE // System.out.println("instance2 = " + instance2);// INSTANCE } } /*
可以看到,EnumSingle 本身也是 class,只是继承了 Enum 类。
这里还是有空参构造。可是运行时报错说没有空参构造。骗子 + 2。
javap -p EnumSingle.class
Compiled from "EnumSingle.java"
public final class com.geek.singleton.EnumSingle extends java.lang.Enum<com.geek.singleton.EnumSingle> {
public static final com.geek.singleton.EnumSingle INSTANCE;
private static final com.geek.singleton.EnumSingle[] $VALUES;
public static com.geek.singleton.EnumSingle[] values();
public static com.geek.singleton.EnumSingle valueOf(java.lang.String);
private com.geek.singleton.EnumSingle();
public com.geek.singleton.EnumSingle getInstance();
static {};
}
使用 jad 反编译。
https://varaneckas.com/jad/jad158g.win.zip
jad -sjava EnumSingle.class。
可以看到 有参构造器。
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: EnumSingle.java
package com.geek.singleton;
public final class EnumSingle extends Enum
{
public static EnumSingle[] values()
{
return (EnumSingle[])$VALUES.clone();
}
public static EnumSingle valueOf(String name)
{
return (EnumSingle)Enum.valueOf(com/geek/singleton/EnumSingle, name);
}
private EnumSingle(String s, int i)
{
super(s, i);
}
public EnumSingle getInstance()
{
return INSTANCE;
}
public static final EnumSingle INSTANCE;
private static final EnumSingle $VALUES[];
static
{
INSTANCE = new EnumSingle("INSTANCE", 0);
$VALUES = (new EnumSingle[] {
INSTANCE
});
}
}
*/
本文地址:https://blog.csdn.net/lyfGeek/article/details/107678359