java设计模式----单例模式

目录

    1.使用单例模式的作用

  2.常见的应用场景

  3.单例模式的实现方式

  4.单例模式存在的问题及解决办法

    5.总结

一、使用单例模式的作用

  

  单例模式:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

二、常见的应用场景

  1.Windows的任务管理器

  2.Windows的回收站

  3.操作系统的文件系统

  4.Servlet 中的Application作用域

  5.Spring中的每个Bean都是单例

  6.Servlet中每个Servlet都是单例

三、单例模式的实现方式

  常见的五种实现方式:

  1.饿汉式(线程安全,调用效率高。但是不能延迟加载)

  2.懒汉式(线程安全,调用效率不高,但是可以延迟加载)

  3.双重检测锁式(由于JVM底层内部原因,偶尔会出现问题,不建议使用)

  4.静态内部类式(线程安全,调用效率高,可以延迟加载)

  5.枚举单例(线程安全,调用效率高,不能延迟加载)

  1、饿汉式

  

 1 package com.demo.singleton;
 2 /**
 3  * 饿汉式单例设计模式实现
 4  */
 5 public class Singleton1 {
 6     
 7     private static Singleton1 singleton = new Singleton1();
 8     //私有化构造方法
 9     private Singleton1(){}
10     public static Singleton1 getSingleton(){
11         return singleton;
12     }
13 }

  

  2、懒汉式

 1 package com.demo.singleton;
 2 /**
 3  * 懒汉式单例设计模式实现
 4  */
 5 public class Singleton2 {
 6     
 7     private static Singleton2 singleton;
 8     //私有化构造方法
 9     private Singleton2(){}
10     public synchronized static Singleton2 getSingleton(){
11         if(singleton==null){
12             singleton = new Singleton2();
13         }
14         return singleton;
15     }
16 }

  3、双重检测锁(不推荐)

 1 package com.demo.singleton;
 2 /**
 3  * 双重检测锁实现单例设计模式
 4  */
 5 public class Singleton3 {
 6     
 7     private static Singleton3 singleton=null;
 8     //私有化构造方法
 9     private Singleton3(){}
10     public static Singleton3 getSingleton(){
11         if(singleton==null){
12             Singleton3 sc;
13             synchronized(Singleton3.class){
14                 sc = singleton;
15                 if(sc == null){
16                     synchronized(Singleton3.class){
17                         if(sc == null){
18                             sc = new Singleton3();
19                         }
20                     }
21                     singleton=sc;
22                 }
23             }
24         }
25         return singleton;
26     }
27 }

  4、静态内部类实现方式

 1 package com.demo.singleton;
 2 /**
 3  * 静态内部类实现单例设计模式
 4  */
 5 public class Singleton4 {
 6     private static class singleton{
 7         private static final Singleton4 singleton= new Singleton4(); 
 8     }
 9     private Singleton4(){}
10     public static Singleton4 getSingleton(){
11         return singleton.singleton;
12     }
13 }

  5、使用枚举实现单例模式

 1 package com.demo.singleton;
 2 /**
 3  * 使用枚举实现单例模式
 4  */
 5 public enum Singleton5 {
 6     /**
 7      * 定义一个枚举元素,这个枚举元素本身就代表着一个单例
 8      */
 9     SINGLETON;
10     //定义这个单例的操作
11     public Singleton5 getSingleton(){
12         return SINGLETON;
13     }
14 }

四、单例模式出存在的问题及解决办法

  问题:

    1.反射可以破解上面几种单例模式(不包含枚举式)

 

package com.demo.singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 利用反射破解Singleton2单例
 * @author Administrator
 *
 */
public class Client {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException  {
        //不使用反射破解的情况下
        Singleton2 s1 = Singleton2.getSingleton();
        Singleton2 s2 = Singleton2.getSingleton();
        System.out.println(s1==s2);
        //使用反射破解
        Class<Singleton2> clazz = (Class<Singleton2>) Class.forName("com.demo.singleton.Singleton2");
        Constructor<Singleton2> constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton2 i1 = constructor.newInstance();
        Singleton2 i2 = constructor.newInstance();
        System.out.println(i1==i2);
    }
}

解决方案:

在Singleton2类中的私有化构造函数中判断是否存在这个对象,如果存在则手动抛出一个异常。

package com.demo.singleton;
/**
 * 懒汉式单例设计模式实现
 */
public class Singleton2 {
    
    private static Singleton2 singleton;
    //私有化构造方法
    private Singleton2() throws Exception{
        if(singleton!=null){
            throw new Exception("只能创建一个实例");
        }
    }
    public synchronized static Singleton2 getSingleton() throws Exception{
        if(singleton==null){
            singleton = new Singleton2();
        }
        return singleton;
    }
}

    2.反序列化可以破解上面几种单例模式(不包含枚举式)

package com.demo.singleton;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * 使用反序列化破解单例模式
 * @author Administrator
 *
 */
public class Client2 {
    public static void main(String[] args) throws Exception {
        //序列化单例类
        Singleton2 s =Singleton2.getSingleton();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("b.obj"));
        oos.writeObject(s);
        oos.flush();
        oos.close();
        //反序列化单例
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("b.obj"));
        Singleton2 s2 = (Singleton2)ois.readObject();
        ois.close();
        System.out.println(s==s2);
    }
}

解决方案:

在序列化的方法里面定义一个readResolve()方法,在反序列化的时候会回调这个函数。

package com.demo.singleton;

import java.io.ObjectStreamException;
import java.io.Serializable;

/**
 * 懒汉式单例设计模式实现
 */
public class Singleton2 implements Serializable{
    
    private static Singleton2 singleton;
    //私有化构造方法
    private Singleton2() throws Exception{
        if(singleton!=null){
            throw new Exception("只能创建一个实例");
        }
    }
    public synchronized static Singleton2 getSingleton() throws Exception{
        if(singleton==null){
            singleton = new Singleton2();
        }
        return singleton;
    }
    private Object readResolve() throws ObjectStreamException{
        return singleton;
    }
}

 五、总结

  单例模式的优点:

  ——由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动

时直接产生一个单例对象,然后永久驻留内存的方式来解决

  ——单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理

  具体使用哪种单例模式:

  – 单例对象 占用 资源 少,不需要 延时加载:

  • 枚举式 好于 饿汉式

  – 单例对象 占用 资源 大,需要 延时加载:

  • 静态内部类式 好于 懒汉式

原文地址:https://www.cnblogs.com/bananafish/p/9940709.html