欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java设计模式之单例模式,笔记完整到不敢想象

程序员文章站 2022-10-01 11:40:17
单例模式: 保证一个类只有一个实例,并且提供一个访问该实例的全局访问入口 1.Windows的任务管理器2.Windows的回收站,也是一个单例应用3.项目中的读取配置文件的对象4.数据库的连接池5.Servlet中的Application Servlet6.Spring中的Bean默认也是单例的7 ......

单例模式:

作用

保证一个类只有一个实例,并且提供一个访问该实例的全局访问入口

单例模式的常用

1.windows的任务管理器
2.windows的回收站,也是一个单例应用
3.项目中的读取配置文件的对象
4.数据库的连接池
5.servlet中的application servlet
6.spring中的bean默认也是单例的
7.springmvc struts中的控制器

单例模式的优点

1.由于单例模式只生成一个实例,减少了系统给的性能开销,当一个对象需要产生时,当时消耗的资源较多。那么产生对象时构建的方式就可以通过单例去构建。
2.单例模式存在全局访问点,所以可以优化共享资源访问。

 

常见的单例模式的构建方法:

1.饿汉式:线程安全 调用率高  但是不能延迟加载
2.懒汉式:线程安全 调用率不高 但是可以延迟加载
3.双重检测(double check )
4.静态内部类(线程安全 可以延迟加载)
5.枚举单例 线程安全 不可以延迟加载

 

代码案例展示

饿汉式

/** 
* 饿汉式: 
*      类只要被加载就会被加载全局变量,所以饿汉式,会被及时加载。(没有懒加载 ) 
*      并且存在天然的线程安全问题。 
* @author 码歌老薛 
* @date 创建时间 猴年马月 
* @version 1.0 
*/  
public class singlehungry {  
 //提供静态的全局变量 作为访问该类实例的入口  
 private static singlehungry sh = new singlehungry();  
 /** 
* 构造器私有 无法创建对象 
 */  
 private singlehungry(){  
       
}  
 /** 
* 对外提供get方法获取 该类的实例 
* @return 
 */  
public static singlehungry getinstance(){  
return sh;  
 }  
}  

  

 

懒汉式

/** 
 * 懒汉式: 
*      全局变量初始化放到了实例化方法中,延迟产生对象。 
 *      但是当多个线程统一访问时,有可能出现线程不安全的情况。需要优化。 
 * @author 码歌老薛 
 * @date 创建时间 猴年马月 
 * @version 1.0 
*/  
public class singlelazy implements serializable{  
   //提供静态的全局变量 作为访问该类实例的入口 但是这里不立即加载  
    private static singlelazy sh = null;  
      
     
    /** 
     * 构造器私有 无法创建对象 
     */  
    private singlelazy(){   
       system.out.println("构造函数被调用了");  
   }  
      
   /** 
     * 对外提供get方法获取 该类的实例 
    * @return 
    * @throws interruptedexception  
     */  
    public static synchronized singlelazy getinstance() {  
       if(sh==null){  
            sh = new singlelazy();  
       }  
        return sh;  
         
    }  
        
} 

上海尚学堂java培训 shsxt.com
 

双重检测

/** 
 * 懒汉式: 
 *      全局变量初始化放到了实例化方法中,延迟产生对象。 
 *      但是当多个线程统一访问时,有可能出现线程不安全的情况。需要优化。 
 * @author 码歌老薛 
 * @date 创建时间 猴年马月 
 * @version 1.0 
 */  
public class singlelazy4 {  
    //提供静态的全局变量 作为访问该类实例的入口 但是这里不立即加载  
    private volatile  static singlelazy4 sh = null;  
      
      
    /** 
     * 构造器私有 无法创建对象 
     */  
    private singlelazy4(){  
        system.out.println("被调用了");  
    }  
      
    /** 
     * 双重校验锁式(也有人把双重校验锁式和懒汉式归为一类)分别在代码锁前后进行判空校验 
     * ,双重校验锁式是线程安全的。然而,在jdk1.5以前,dcl是不稳定的,有时也可能创建多个实例, 
     * 在1.5以后开始提供volatile关键字修饰变量来达到稳定效果。 
     * 双重校验锁dcl(double checked locking) 
     * @return 
     * @throws interruptedexception  
     */  
    public static singlelazy4 getinstance() {  
        if(sh == null){  
            synchronized(singlelazy4.class){  
                if(sh == null){  
                    sh = new singlelazy4();  
                   //return singleton;    //有人提议在此处进行一次返回  
                }  
                //return singleton;    //也有人提议在此处进行一次返回  
            }  
        }  
        return sh;  
    }  
}  

上海尚学堂java培训 shsxt.com 获取更多java学习资料

 

 

静态内部类

/** 
 *静态内部类 
 *
 * @author 码歌老薛
 * @date 创建时间 猴年马月 
 * @version 1.0 
 */  
public class singleinner {  
      
    /** 
     *静态内部类式和饿汉式一样,同样利用了classloader的机制保证了线程安全; 
     *不同的是,饿汉式在singleton类被加载时(从代码段3-2的class.forname可见) 
     *就创建了一个实例对象,而静态内部类即使singleton类被加载也不会创建单例对象, 
     *除非调用里面的getinstance()方法。因为当singleton类被加载时 
     *,其静态内部类singletonholder没有被主动使用。只有当调用getinstance方法时, 
     *才会装载singletonholder类,从而实例化单例对象。 
 
    这样,通过静态内部类的方法就实现了lazy loading,很好地将懒汉式和饿汉式结合起来, 
    既实现延迟加载,保证系统性能,也能保证线程安全  
     */  
    private static class singleinnerholder{  
        private static singleinner instance = new singleinner();  
    }  
      
    private singleinner(){  
        system.out.println("我被调用了");  
   }  
    public static singleinner getinstance(){  
        return singleinnerholder.instance;  
    }  
}  

 

 

枚举单例

/** 
 * jvm提供底层保证  
 * 不可能出现序列化、反射产生对象的漏洞 但是不能做到延迟加载 
在外部,可以通过enumsingleton.instance.work()来调用work方法。默认的枚举实例的创建是线程安全的 
、,但是实例内的各种方法则需要程序员来保证线程安全。 
总的来说,使用枚举单例模式,有三个好处: 
 1.实例的创建线程安全,确保单例。2.防止被反射创建多个实例。3.没有序列化的问题。 
 * @author 码歌老薛 
 * @date 创建时间 猴年马月 
 * @version 1.0 
 */  
public enum singleenum {  
    //实例化对象  
    instance;  
      
    /** 
     * 对象需要执行的功能 
     */  
    void getinstance(){  
          
    }  
}  

上海尚学堂java培训 shsxt.com
 

反射/序列化 获取对象 以及防止方式

import java.io.objectstreamexception;  
import java.io.serializable;  
  
/** 
 * 懒汉式: 
 *      全局变量初始化放到了实例化方法中,延迟产生对象。 
 *      但是当多个线程统一访问时,有可能出现线程不安全的情况。需要优化。 
 * @author 码歌老薛 
 * @date 创建时间 猴年马月 
 * @version 1.0 
 */  
public class singlelazy implements serializable{  
    //提供静态的全局变量 作为访问该类实例的入口 但是这里不立即加载  
   private static singlelazy sh = null;  
      
      
    /** 
     * 构造器私有 无法创建对象 
     */  
    private singlelazy(){  
       if(sh!=null){  
            throw new runtimeexception();  
        }  
        system.out.println("构造函数被调用了");  
    }  
      
    /** 
     * 对外提供get方法获取 该类的实例 
     * @return 
     * @throws interruptedexception  
     */  
    public static synchronized singlelazy getinstance() {  
        if(sh==null){  
            sh = new singlelazy();  
        }  
        return sh;  
          
    }  
      
    private object readresolve()throws objectstreamexception{  
        return sh;  
    }  
      
}  

上海尚学堂java培训 shsxt.com
 

用法总结:

1、懒汉式效率是最低的。
2、占用资源少 不需要延时加载  枚举优于 饿汉式
3、占用资源比较多 需要延时加载 静态内部类 优于  懒汉式

更多java技术文章欢迎阅读上海尚学堂java培训,免费试学和线上公开课培训课程等你学习。