zoukankan      html  css  js  c++  java
  • 设计模式之(一)单例模式(Singleton)

      在一个软件系统中,经常有有些特殊的对象就需要一个实例,如果有多个的话,就比较浪费服务器资源,最典型的就是 整个系统的配置文件对象。

           普通方式读取配置文件

    // 配置文件 SingletonApp.properies
     
    paramA2 = AAAAAA
    paramB2 =BBBBBBBBBBB
    
    
    /***************   AppConfig   **************************************/
    
    package designPattern_1_Singleton;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Properties;
    
    public class AppConfig { 
        private String ParamA;
        private String ParamB;
    
        public String getParamA() {
            return ParamA;
        }
    
        public String getParamB() {
            return ParamB;
        }
        
        public AppConfig()
        {
            readConfig();
        }
        
        private void readConfig()
        {
            Properties prop = new Properties();
            InputStream in = null;
            try{
                in = this.getClass().getResourceAsStream("SingletonApp.properties");
                
                prop.load(in);
                this.ParamA = prop.getProperty("paramA2");
                this.ParamB = prop.getProperty("paramB2");
                
            }
            catch(IOException e){
                System.out.println("加载配置文件出错了,详细信息参考以下");
                e.printStackTrace();
            }
            finally{
                try { 
                    in.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace(); 
                };
            }
        }
    
    }
    
    
    
    /********************  Client.java   *****************************/
    package designPattern_1_Singleton;
    
    public class Client {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
            AppConfig app = new AppConfig();
    
            String paramA = app.getParamA();
            String paramB = app.getParamB();
    
            System.out.println("paramA = " + paramA + " paramB = " + paramB);
    
        }
    
    }
    不用单例模式实现

            上面是不用单例模式实现的功能。那么这段代码有什么问题吗?在实现功能方面来说,是完成了功能,但是设计不够合理,这样做没用一次,就要 new 一个config 类。而且发现所有的 Config 类内容都是一样的。这样的话,就存在浪费资源的,这样就有了优化的方面。

            优化的内容就是所有需要用到配置的内容都公用一个 Config 类,这样的话就大大节省了资源。从这个功能的描述我们再抽象一下,就引出了单例模式,下面我们来看一下单例模式的定义:

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

            这样就要保证在整个系统中保证需要用单例模式的类的构造函数只能执行一次。根据这个思路把上面的例子用单例模式改造一下。

       

      1 package designPattern_1_Singleton;
      2 
      3 import java.io.IOException;
      4 import java.io.InputStream;
      5 import java.util.Properties;
      6 
      7 public class SingletonAppConfig {
      8     
      9     /**
     10      * 定义一个静态变量,来存储创建的类的实例,因为是静态的,所以只能创建一次。
     11      */
     12     private static SingletonAppConfig instance = new SingletonAppConfig();
     13     
     14     /**
     15      * 用来提供外界实例类的函数,因为构造函数私有了,那么返回存放 实例的变量的方式来床创建类。
     16      * @return SingletonAppConfig 的实例
     17      */
     18     public static SingletonAppConfig getInstance(){
     19         return instance;
     20     }
     21     
     22     //为了只有一个实例,把构造函数改成私有
     23     private SingletonAppConfig()
     24     {
     25         readConfig();
     26     }
     27     
     28     private String ParamA;
     29     private String ParamB;
     30 
     31     public String getParamA() {
     32         return ParamA;
     33     }
     34 
     35     public String getParamB() {
     36         return ParamB;
     37     }
     38     
     39     private void readConfig()
     40     {
     41         Properties prop = new Properties();
     42         InputStream in = null;
     43         try{
     44             in = this.getClass().getResourceAsStream("SingletonApp.properties");
     45             
     46             prop.load(in);
     47             this.ParamA = prop.getProperty("paramA2");
     48             this.ParamB = prop.getProperty("paramB2");
     49             
     50         }
     51         catch(IOException e){
     52             System.out.println("加载配置文件出错了,详细信息参考以下");
     53             e.printStackTrace();
     54         }
     55         finally{
     56             try { 
     57                 in.close();
     58             } catch (IOException e) {
     59                 // TODO Auto-generated catch block
     60                 e.printStackTrace(); 
     61             };
     62         }
     63     }
     64 }
     65 
     66 
     67 
     68 *****************************************************************
     69 
     70 
     71 package designPattern_1_Singleton;
     72 
     73 public class Client2 {
     74 
     75     public static void main(String[] args) {
     76         // TODO Auto-generated method stub
     77         SingletonAppConfig sConfig = SingletonAppConfig.getInstance();
     78         for(int i=0;i<=300;i++)
     79         {
     80             sConfig = SingletonAppConfig.getInstance();
     81             System.out.println(sConfig);
     82         }
     83         System.out.println(sConfig.getParamA()+" **** "+sConfig.getParamB());
     84     }
     85 }
     86 
     87 **************************************************************
     88 designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
     89 designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
     90 designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
     91 designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
     92 designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
     93 designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
     94 designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
     95 designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
     96 designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
     97 designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
     98 designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
     99 designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
    100 designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
    101 designPattern_1_Singleton.SingletonAppConfig@4e8a88a9
    102 AAAAAA **** BBBBBBBBBBB
    Singleton_1

      通过以上代理,也实现了读取配置文件的功能,但是明显修改后性能更好,因为修改后,这个类在系统中无论在什么地方调用,都只能实现一个实例,能够节省很多性能,改造后的就是单例模式。

      单例模式整体上有两种实现方式,一种就是上面的这种,饿汉模式,另外一种是懒汉模式。

      那么怎么区分饿汉模式和懒汉模式呢,上面实现的是饿汉模式,下面贴出来懒汉模式,然后在区分下:

     1 package designPattern_1_Singleton;
     2 
     3 import java.io.IOException;
     4 import java.io.InputStream;
     5 import java.util.Properties;
     6 
     7 /*
     8  * 懒汉式实现办法
     9  */
    10 public class Singleton2AppConfig {
    11 
    12     private static Singleton2AppConfig s2Config = null;
    13     
    14     public Singleton2AppConfig getInstance(){
    15         //判断是否创建了实例,如果没有则创建
    16         if(s2Config == null){
    17             s2Config = new Singleton2AppConfig();
    18         }
    19         return s2Config;
    20     }
    21     
    22     private Singleton2AppConfig(){
    23         readConfig();
    24     }
    25     
    26     private String ParamA;
    27     private String ParamB;
    28 
    29     public String getParamA() {
    30         return ParamA;
    31     }
    32 
    33     public String getParamB() {
    34         return ParamB;
    35     }
    36     
    37     private void readConfig()
    38     {
    39         Properties prop = new Properties();
    40         InputStream in = null;
    41         try{
    42             in = this.getClass().getResourceAsStream("SingletonApp.properties");
    43             
    44             prop.load(in);
    45             this.ParamA = prop.getProperty("paramA2");
    46             this.ParamB = prop.getProperty("paramB2");
    47             
    48         }
    49         catch(IOException e){
    50             System.out.println("加载配置文件出错了,详细信息参考以下");
    51             e.printStackTrace();
    52         }
    53         finally{
    54             try { 
    55                 in.close();
    56             } catch (IOException e) {
    57                 // TODO Auto-generated catch block
    58                 e.printStackTrace(); 
    59             };
    60         }
    61     }
    62 }
    Singleton_2

       通过 Singleton_1 与 Singleton_2 比较,两个模式的本质区别是:创建对象实例的先后。

       饿汉式单例模式,形象的说就是很饿,要急吼吼的创建实例,急到加载类的时候就创建了实例对象。关键代码是

    1 /**
    2  * 定义一个静态变量,来存储创建的类的实例,因为是静态的,所以只能创建一次。
    3 */
    4 private static SingletonAppConfig instance = new SingletonAppConfig();

           懒汉式单例模式,很实例的创建拖拖拉拉,能不创建就不创建。直到需要使用的时候再去进行了创建,关键代码是:

    1 private static Singleton2AppConfig s2Config = null;
    2     
    3 public static Singleton2AppConfig getInstance(){
    4     //判断是否创建了实例,如果没有则创建
    5     if(s2Config == null){
    6         s2Config = new Singleton2AppConfig();
    7     }
    8     return s2Config;
    9 }

         通过以上要点总结下单例模式的要点:

       1、因为类的性质,他所有实例都是相同的,因此控制这个类只能有一个实例对象。

       2、定义一个私有静态变量,存放实例后的类,因为是静态变量。

       3、类的改造函数设置为私有,不让外界通过构造函数创建实例对象。

       4、但是使用类总是要实例的,所以就提供一个共有的静态方法来创建实例对象。但是为了让类能够使用这个方法创建对象,所以这个类必须要是静态方法。

            5、通过提供的静态方法(一般都是 getInstance)来创建类实例(因为普通方法需要先实例后才能调用,所以这个方法必须是静态方法)。

      懒汉式和饿汉式模式区别

             1、从时间和空间方面来说,懒汉式是因为用的时候才创建实例,而且在每次创建类的时候都需要判断,所以是时间空间模式。而饿汉式模式是每次装载类的时候就要进行实例,因此是空间换时间。

             2、从线程安全方面来讲。饿汉式因为是加载类的时候就实例化了,而虚拟机保证加载类只会加载一次,因此是线程安全的。但是装载类和实例类不同步的懒汉式则是不安全的。

             

     1 private static Singleton2AppConfig s2Config = null;
     2     
     3 public static Singleton2AppConfig getInstance(){
     4     //判断是否创建了实例,如果没有则创建
     5     if(s2Config == null){
     6         s2Config = new Singleton2AppConfig();
     7     }
     8         
     9     return s2Config;
    10 }

     

          这个问题就是要用加锁的方式来解决

         

     1 /**
     2   * volatile 修改符保证这个类每次都是执行 volatile 
     3   */
     4 private volatile static Singleton2AppConfig s2Config = null;
     5     
     6 public static Singleton2AppConfig getInstance(){
     7     //判断是否创建了实例,如果没有则创建
     8     if(s2Config == null){
     9         synchronized (Singleton2AppConfig.class) {
    10             s2Config = new Singleton2AppConfig();
    11         }
    12     }
    13         
    14     return s2Config;
    15 }

      加锁后由于只是在第一次创建类使用同步锁功能,因此影响也不大。

      实例模式的变种,可能创建指定数量的实例

               单例模式的本质是控制类实例化的数量,那么如果单例的这个类如果使用频率高到一个实例不够使用了,那么是否可以达到指定需要的实例数量呢?这个是可以的。实例化有一定的数量后,调用肯定也是有特殊的算法,这里我们不探究调用的算法,只看如果实现指定数量的类实例。看代码:

     1 package designPattern_1_Singleton;
     2 
     3 import java.util.HashMap;
     4 import java.util.Map;
     5 
     6 public class Singleton3AppConfig {
     7     private final static String CACHE = "cache";
     8 
     9     private static Map<String, Singleton3AppConfig> map = new HashMap<String, Singleton3AppConfig>();
    10 
    11     private static int MAX = 3;
    12 
    13     private static int num = 1;
    14 
    15     private Singleton3AppConfig() {
    16         
    17     }
    18 
    19     public static Singleton3AppConfig getInstance() {
    20         String classkey = CACHE + num;
    21         if (map.get(classkey) == null) {
    22             
    23             map.put(classkey, new Singleton3AppConfig());
    24         }
    25         num += 1;
    26         if (num > MAX) {
    27             num = 1;
    28         }
    29 
    30         return map.get(classkey);
    31     }
    32 }
    33 
    34 
    35 
    36 /***********************8*************************************/
    37 
    38 package designPattern_1_Singleton;
    39 
    40 public class Client3 {
    41 
    42     public static void main(String[] args) {
    43         // TODO Auto-generated method stub
    44         for(int i=0;i<30;i++){
    45             Singleton3AppConfig s3c = Singleton3AppConfig.getInstance();
    46             System.out.println(s3c);
    47         }
    48         
    49     }
    50 
    51 }
    52 
    53 /***********************8*************************************/
    54 
    55 designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
    56 designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
    57 designPattern_1_Singleton.Singleton3AppConfig@40671416
    58 designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
    59 designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
    60 designPattern_1_Singleton.Singleton3AppConfig@40671416
    61 designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
    62 designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
    63 designPattern_1_Singleton.Singleton3AppConfig@40671416
    64 designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
    65 designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
    66 designPattern_1_Singleton.Singleton3AppConfig@40671416
    67 designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
    68 designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
    69 designPattern_1_Singleton.Singleton3AppConfig@40671416
    70 designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
    71 designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
    72 designPattern_1_Singleton.Singleton3AppConfig@40671416
    73 designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
    74 designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
    75 designPattern_1_Singleton.Singleton3AppConfig@40671416
    76 designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
    77 designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
    78 designPattern_1_Singleton.Singleton3AppConfig@40671416
    79 designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
    80 designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
    81 designPattern_1_Singleton.Singleton3AppConfig@40671416
    82 designPattern_1_Singleton.Singleton3AppConfig@5f4fcc96
    83 designPattern_1_Singleton.Singleton3AppConfig@7000bcbc
    84 designPattern_1_Singleton.Singleton3AppConfig@40671416

     这个是单例模式变种,代码很好理解。这儿看执行效果,虽然调用的地方实例了30个,但是从打印内容来看,只有三个 实例化的地址。所以知识实例化了指定的三个实例。(这里补充一点:如果构造函数没有返回一个字符串,那么如果直接输出类的话,就输出的是他的地址

        小结

       单例模式是整个设计模式中最好理解,也是在使用场景中最容易分辨使用的设计模式。使用单例模式不会因为使用设计模式让代码变得较复杂。而他的目的就是为了在让合适的类只有一个实例对象,从而达到节省资源开支的目的。

  • 相关阅读:
    Python_时间,日期,时间戳之间转换
    VirtualBox虚拟机网络设置
    Java_IO流
    获取ElasticSearch索引列表
    关闭ElasticSearch动态创建mapping
    关于elasticsearch输出默认限制最多一万条记录的问题
    linux下ElasticSearch安装及集群搭建
    linux下NFS远程目录挂载
    linux centos7 防火墙及端口开放相关命令
    linux命令
  • 原文地址:https://www.cnblogs.com/pengweiqiang/p/10409253.html
Copyright © 2011-2022 走看看