Featured image of post Java设计模式-单例模式Singleton

Java设计模式-单例模式Singleton

相关文章

【合集】Java设计模式

应用

{@link Runtime#getRuntime()}

实现方式

静态成员变量(饿汉式)

1
2
3
4
5
6
7
8
9
public static class Singleton1 {
   private static final Singleton1 INSTANCE = new Singleton1();
   private Singleton1() {
   }

   public static Singleton1 getInstance() {
      return INSTANCE;
   }
}

静态代码块(饿汉式)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public static class Singleton2 {
    private static final Singleton2 INSTANCE;
    private Singleton2() {
    }
    static {
        INSTANCE = new Singleton2();
    }

    public static Singleton2 getInstance() {
        return INSTANCE;
    }
}

枚举(饿汉式)

1
2
3
public enum Singleton3 {
   INSTANCE;
}

双重检查锁(懒汉式)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public static class Singleton4 {
   // 不加volatile修饰,多线程下,JVM的指令重排序有可能会导致空指针
   // 添加volatile的双重检查锁模式是一种比较好的单例实现模式,解决了单例、性能、多线程安全问题。
   private static volatile Singleton4 instance;
   private Singleton4() {
   }
   public static Singleton4 getInstance() {
      if (instance != null) {
         return instance;
      }
      synchronized (Singleton4.class) {
         if (instance != null) {
            return instance;
         }
         instance = new Singleton4();
         return instance;
      }
   }
}

静态内部类(懒加载)

JVM在加载外部类时不加载静态内部类,静态内部类在属性/方法被调用时才会被加载, 并初始化其静态属性。

静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。

优秀的单例模式,开源项目常用。

不加锁的情况下,保证了多线程下的安全,没有任何性能影响和空间的浪费。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public static class Singleton5 {
    private Singleton5() {
    }
    private static class SingletonHolder {
        private static final Singleton5 INSTANCE = new Singleton5();
    }

    public static Singleton5 getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

序列化会破坏单例

解决方案

单例类中添加readResolve()方法,在反序列化时被反射调用,

如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public static class Singleton implements Serializable {
    private static final Singleton INSTANCE = new Singleton();
    private Singleton() {
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }

    /**
     * 在Singleton类中添加 readResolve 方法后 desc.hasReadResolveMethod() 方法执行结果为true
     * 反射调用单例类中的 readResolve 方法 Object rep = desc.invokeReadResolve(obj);
     */
    @Serial
    private Object readResolve() {
        return INSTANCE;
    }
}

原理

逐步进入源码

1
2
3
4
{@link ObjectInputStream#readObject(Class)}
{@link ObjectInputStream#readObject0(Class, boolean)}
return checkResolve(readOrdinaryObject(unshared));
{@link ObjectInputStream#readOrdinaryObject(boolean)}

测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static class Client {
    public static void main(String[] args) throws Exception {
        writeObjectToFile();

        Singleton s1 = readObjectFromFile();
        Singleton s2 = readObjectFromFile();
        System.out.println(s1 == s2); // true
    }

    // 序列化方法
    private static void writeObjectToFile() throws Exception {
        try (
           ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(Client.class.getResource("/").getPath() + "test" + File.separator + "a.txt"));
        ) {
            Singleton instance = Singleton.getInstance();
            oos.writeObject(instance);
        }
    }

    // 反序列化方法
    private static Singleton readObjectFromFile() throws Exception {
        try (
          ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Client.class.getResource("/").getPath() + "test" + File.separator + "a.txt"));
        ) {
            Singleton instance = (Singleton) ois.readObject();
            return instance;
        }
    }
}

反射会破坏单例

解决方案

反射第二次构造,INSTANCE肯定不为空抛出异常。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public static class Teacher {
    private static final Teacher INSTANCE = new Teacher();

    private Teacher() {
        // 解决方法,反射第二次构造,INSTANCE肯定不为空抛出异常。
        if (INSTANCE != null) {
            throw new RuntimeException("禁止反射破坏单例");
        }
    }

    public static Teacher getInstance() {
        return INSTANCE;
    }
}

测试

1
2
3
4
5
public static void main(String[] args) throws Exception {
      Constructor<Teacher> declaredConstructor = Teacher.class.getDeclaredConstructor();
      declaredConstructor.setAccessible(true);
      System.out.println(declaredConstructor.newInstance() == declaredConstructor.newInstance()); // true
}
皖ICP备2024056275号-1
发表了78篇文章 · 总计148.99k字
本站已稳定运行