Java设计模式笔记(1)单例模式

声明:此笔记为B站up主“程序员大表哥”所授课程【23种java设计模式】个人学习笔记,仅供学习,不做他用。
链接:https://www.bilibili.com/video/av81810102

一.单例模式

1.懒汉模式

延迟加载,只有在真正使用的时候, 才开始实例化。
1).线程安全问题
2) .double check加锁优化
3).编译器(JIT),CPU 有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile 关键字进行修饰,对于volatile修饰的字段,可以防止指令重排。

class lazySingleton {
    //声明但是不初始化,懒汉
    private volatile static lazySingleton instance;
    private lazySingleton(){}
    public static lazySingleton getInstance() {
        if (instance == null) {
           synchronized (lazySingleton.class){
               if (instance==null){
                   /*
                   字节码层面
                   JIT,JVM
                   1.分配空间
                   ps:2,3可能会颠倒顺序,有可能会发生空指针错误。 volatile解决此类问题
                   2.引用赋值
                   3.初始化
                    */
                   instance=new lazySingleton();
               }
           }
        }
        return instance;
    }
}

2.饿汉模式

类加载的初始化阶段就完成了实例的初始化。本质上就是借助于JVM类加载机制,保证实例的唯一性。
类加载过程:
1,加载二进制数据到内存中,生成对应的Class数据结构,
2,连接: a.验证,b.准备(给类的静态成员变量赋 默认值),c.解析
3,初始化:给类的静态变量赋 初值
只有在真正使用对应的类时,才会触发初始化如( 当前类是启动类即main函数所在类,直接进行new操作,访问静态属性、访问静态方法,用反射访问类,初始化一个类的子类等.)

class hungrySingleton{
    //声明且初始化,饿汉,通过JVM加载机制保证单例
    private static hungrySingleton instance=new hungrySingleton();
    private hungrySingleton(){

    }

    public static hungrySingleton getInstance() {
        return instance;
    }
}

3.静态内部类实现

1).本质上是利用类的加载机制来保证线程安全
2).只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一种形式。

class InnerClassSingleton{
  private static class InnerClassSingletonHolder{
      private static InnerClassSingleton instance=new InnerClassSingleton();
    }
    private InnerClassSingleton(){}

    public static InnerClassSingleton getInstance() {
        return InnerClassSingletonHolder.instance;
    }
}

4.反射攻击

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        反射攻击
        Constructor<InnerClassSingleton> declaredConstructor
            =InnerClassSingleton.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        InnerClassSingleton instance=declaredConstructor.newInstance();
        InnerClassSingleton instance2=InnerClassSingleton.getInstance();
        System.out.println(instance);
        System.out.println(instance2);
    }

解决方法:饿汉模式和静态内部类方式可以在构造函数加判断,抛出异常!防止反射攻击!

 private InnerClassSingleton(){
      if (InnerClassSingletonHolder.instance!=null){
          throw new RuntimeException("Muti-instances is not allowed!");
      }
    }

5.枚举类型实现

1)天然不支持反射创建对应的实例,且有自己的反序列化机制
2)利用类加载机制保证线程安全

public enum EnumSingleton {
    INSTANCE;
    public void print(){
        System.out.println(this.hashCode());
    }
}
//反射实例化测试:不可以!
class EnumTest{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        EnumSingleton enumSingleton = declaredConstructor.newInstance("INSTANCE",0);
    }
}
//报错:Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects

5.序列化

1)可以利用指定方法来替换从反序列化流中的数据如下:

1 //ANY-ACCESS -MODIFIER object readResolve() throws objectStreamException;

5.1内部类反序列化:

//不支持序列化,需要实现Serializable
class InnerClassSingleton implements Serializable{
    //加上serialVersionUID防止兼容对文件的修改
    static final long serialVersionUID = 42L;
  private static class InnerClassSingletonHolder{
      private static InnerClassSingleton instance=new InnerClassSingleton();
    }
    private InnerClassSingleton(){
      //阻止反射实例化第二个instance
      if (InnerClassSingletonHolder.instance!=null){
          throw new RuntimeException("Muti-instances is not allowed!");
      }
    }

    public static InnerClassSingleton getInstance() {
        return InnerClassSingletonHolder.instance;
    }
    //解决序列化后instance不一致的问题
    Object readResolve() throws ObjectStreamException{
        return InnerClassSingletonHolder.instance;
    }
}

测试序列化:

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
        InnerClassSingleton instance=InnerClassSingleton.getInstance();
        /*序列化
       ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("InnerClassSingletonSerialization"));
       oos.writeObject(instance);
       oos.close();
        */
//反序列化
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("InnerClassSingletonSerialization"));
        InnerClassSingleton instance1=(InnerClassSingleton)ois.readObject();
        ois.close();
        System.out.println(instance);
        System.out.println(instance1);
    }

5.2枚举类型反序列化

//天然支持序列化
class EnumTest{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
//        Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class,int.class);
//        declaredConstructor.setAccessible(true);
//        EnumSingleton enumSingleton = declaredConstructor.newInstance("INSTANCE",0);
        EnumSingleton instance=EnumSingleton.INSTANCE;
//        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("EnumSingletonSerialization"));
//        oos.writeObject(instance);
//        oos.close();
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("EnumSingletonSerialization"));
        EnumSingleton instance1=(EnumSingleton) ois.readObject();
        ois.close();
        System.out.println(instance);
        System.out.println(instance1);
    }
}

6.单例模式在源码中的运用

源码中的应用
1//Spring&JDK
2 java. lang . Runtime
3 org. springframework. aop. framework. ProxyFactoryBean
4 org. springframework . beans . factory. support . DefaultSingletonBeanRegistry
5 org. springframework . core . ReactiveAdapterRegistry 
6 // Tomcat
org. apache. catalina. webresources .TomcatURLStreamHandlerFactory
8 //反序列化指定数据源
9 java. util. Currency|

Runtime:

public class Runtime {
    //饿汉模式
    private static Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}
……
                     }