zoukankan      html  css  js  c++  java
  • 设计模式04-单例模式

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

    在java中保证一个类仅有一个实例 就需要先考虑实例化类对象有那些方式?

       常见的有:通过new关键字  反射技术 克隆 

    为了防止通过new关键字和反射技术获得类实例 那么可以通过私有构造方法  

    防止克隆获得类对象的话 可以将类什么为final 类

    常见的两种单例:

         懒汉式:

    /**
     * 懒汉式
     */
    final class SingletonObj{
        private static SingletonObj instance = null ;
        private SingletonObj(){}
        
        public static SingletonObj getInstance(){
            if(instance==null){
                synchronized(SingletonObj.class){
                    instance = new SingletonObj() ;
                    return instance ;
                }
            }else{
                return instance ;
            }
        }
    }

          饿汉式 :

    /**
     *   饿汉式
     */
    final class SingletonObj02{
        private  static SingletonObj02  instance = new SingletonObj02() ;
        private SingletonObj02(){}
        public static SingletonObj02 getInstance(){
            return instance ;
        }
    }

        那么实际开发中什么对象设置为单例呢?

    实例化很耗费性能的对象 线程重量级的对象一般都可以设置为单例对象 例如:数据源  hibernate中的会话工厂SessionFactory 等

    那么下面以数据源的创建为例子 来说明一个单例的实际用法:

      说到数据源的时候常见的有两种:c3p0 dbcp 这两种数据源

      下面采用c3p0数据源做例子:

       需要导入jar包

         c3p0-0.9.2-pre2.jar
         mchange-commons-java-0.2.1.jar

    常见一个DataSource类 提供一个获取c3p0的数据源对象:

       

    package org.lkl.singleton;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    public class DataSource {
        public static ComboPooledDataSource getDataSource() throws Exception{
            ComboPooledDataSource cpds = new ComboPooledDataSource() ;
            cpds.setDriverClass("oracle.jdbc.driver.OracleDriver");
            cpds.setJdbcUrl("jdbc:oracle:thin:@localhost:1521:orcl");
            cpds.setUser("scott");
            cpds.setPassword("tiger");
            cpds.setMinPoolSize(5);
            cpds.setAcquireIncrement(5);
            cpds.setMaxPoolSize(20);
            cpds.setInitialPoolSize(3) ;
            System.out.println("获取数据源");
            return cpds ;
        }
       
    }

     通过上述类可以获得数据源 但如何让这个数据源是一个单例呢?

       创建一个DBManager类  让改类为一个单例 在类的私有构造方法中实例化一个数据源对象  因为该类构造方法私有了 也就只能通过该类提供的全局访问点来获取DBManager的类对象 其对应的构造方法

    只能调用一次 从而数据源也就只会初始化一次 代码如下:

       

    package org.lkl.singleton;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    /**
     * 数据库管理对象
     */
    public class DBManager {
      private static DBManager instance = new DBManager() ;
      private  ComboPooledDataSource cpds = null ;
      /**
       * 在私有的构造中获取数据源 只会执行一次
       */
      private DBManager(){
          try {
            cpds = DataSource.getDataSource() ;
        } catch (Exception e) {
            e.printStackTrace();
        } 
      }
      /**
       * 全局访问点
       */
      public static DBManager getInstance(){
          return instance ;
      }
      
      /**
       * 获取数据库连接
     * @throws SQLException 
       */
      public Connection getConnection() throws SQLException{
          System.out.println(" 获取数据库连接");
          return cpds.getConnection() ;
      }
      /**
       * 关闭数据库连接
       */
      public void close(Connection conn){
          try {
            conn.close() ;
        } catch (SQLException e) {
            e.printStackTrace();
        }
      }
      
       
    }

           测试方法:

    package org.lkl.singleton;
    
    import java.sql.SQLException;
    
    public class Test {
        public static void main(String[] args)  {
                   try {
                       DBManager.getInstance().getConnection() ;
                       DBManager.getInstance().getConnection() ;
                       DBManager.getInstance().getConnection() ;
                       DBManager.getInstance().getConnection() ;
                } catch (SQLException e) {
                    e.printStackTrace();
                }
        }
    }

       获得的结果:

     获取数据源
     获取数据库连接
     获取数据库连接
     获取数据库连接
     获取数据库连接

     很明显达到了数据源的单例.

         通过上面的代码可以发现数据源的配置要设置很多参数 那么一般情况下都会通过配置文件来制定数据源的配置参数的 而不是想上面那么多的setXxx方法来设置 

    以dbcp数据源为例:

         添加jar包:

         commons-dbcp-1.4.jar
       commons-pool-1.6.jar

      为其创建属性文件: jdbc.properties :

       

    username = soctt
    password = tiger
    driverClassName = oracle.jdbc.driver.OracleDriver
    url = jdbc:oracle:thin:@localhost:1521:orcl
    initialSize = 4
    maxActive = 8
    maxIdle = 7
    minIdle = 3
    maxWait = 5000

    那么在DataSource中增加一个方法:getDbcpDataSource :

    package org.lkl.singleton;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.util.Properties;
    
    import org.apache.commons.dbcp.BasicDataSourceFactory;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    public class DataSource {
        public static ComboPooledDataSource getDataSource() throws Exception{
            ComboPooledDataSource cpds = new ComboPooledDataSource() ;
            cpds.setDriverClass("oracle.jdbc.driver.OracleDriver");
            cpds.setJdbcUrl("jdbc:oracle:thin:@localhost:1521:orcl");
            cpds.setUser("scott");
            cpds.setPassword("tiger");
            cpds.setMinPoolSize(5);
            cpds.setAcquireIncrement(5);
            cpds.setMaxPoolSize(20);
            cpds.setInitialPoolSize(3) ;
            System.out.println("获取数据源");
            return cpds ;
        }
        /**
    * 通过配置文件获取数据源配置信息
    */
    public static javax.sql.DataSource getDbcpDataSource() throws Exception{ Properties properties = new Properties(); InputStream inStream = DataSource.class.getClassLoader() .getResourceAsStream("jdbc.properties"); properties.load(inStream); javax.sql.DataSource dataSource = BasicDataSourceFactory.createDataSource(properties); Connection conn = dataSource.getConnection(); System.out.println(conn); return dataSource ; } }

     拓展 :  单例这种技术并没有严格上来说只能创建一个对象  这种技术同样适用于创建固定数量的对象  例如对象池 通过对象池来控制我们的数据库连接池  那么下面来模拟实现一个对象池  

      注意代码中的注释 

    package org.lkl.singleton;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     *对象池
     */
    public class PoolManager {
          
        /**
         *对象池中存放的对象
         */
         private static class PoolItem{
             Object item  ;
             boolean flag = false ; //标记位  如对象从对象池中取出 则为true 反之
             public PoolItem(Object item){
                 this.item = item ;
             }
         }
         /**
          * 以一个集合来保存对象池中的对象
          */
         private List<PoolItem> items = new ArrayList<PoolItem>() ;
         
         /**
          * 往对象池中增加一个对象 
          */
         public void add(Object obj){
             items.add(new PoolItem(obj)) ;
         }
         
         /**
          * 从对象池中获取一个对象
          */
         public Object get(){
             for(int i=0 ;i<items.size() ;i++){
                 PoolItem item = items.get(i) ;
                 if(!item.flag){ //在对象池中
                     item.flag = true ;
                     return item.item ;  //返回   
                 }
             }
             throw new RuntimeException("对象池为空") ;
         }
         
         /**
          * 释放对象
          */
         
         public void release(Object obj){
             for(int i=0 ;i<items.size() ;i++){
                 PoolItem item = items.get(i);
                 if(item==obj){
                     item.flag = false ;
                     break ;
                 }
             }
         }
    }

     通过以上的PoolManager 可以来实现一个ConnectionPool  其代码如下:

    package org.lkl.singleton;
    
    import java.sql.Connection;
    
    /**
     * 数据库连接池
     */
    public class ConnectionPool {
       private static PoolManager pool = new PoolManager() ;
       
       /*
        * 可以通过程序来控制 只能从数据源中获取多少个连接
        */
       public static void addConnectdions(int number){ //number 表示连接池中存放多少个Connection对象
           for(int i=0 ;i<number ;i++){
               try {
                pool.add(org.lkl.singleton.DBManager.getInstance().getConnection()) ;
            } catch (Exception e) {
                e.printStackTrace();
            }
           }
       }
       /**
        * 获取数据库连接
        */
       public static Connection getConnection(){
           return (Connection)pool.get() ;
       }
       /**
        * 释放连接
        * @param c
        */
       public static void releaseConnection(Connection c ){
           pool.release(c) ;
       }
       
    }

      测试代码:

    package org.lkl.singleton;
    
    
    public class Test {
        static{  //这里很重要
            ConnectionPool.addConnectdions(5) ; //初始化只能获取5个数据库连接
        }
        public static void main(String[] args)  {
                   try {
                       ConnectionPool.getConnection() ;
                       ConnectionPool.getConnection() ;
                       ConnectionPool.getConnection() ;
                       ConnectionPool.getConnection() ;
                       ConnectionPool.getConnection() ;
                       ConnectionPool.getConnection() ;
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }
    }
    由于上面试图获取6个连接 从而导致了异常 :结果如下: 

    获取数据源
    获取数据库连接
    获取数据库连接
    获取数据库连接
    获取数据库连接
    获取数据库连接
    java.lang.RuntimeException: 对象池为空
    at org.lkl.singleton.PoolManager.get(PoolManager.java:44)
    at org.lkl.singleton.ConnectionPool.getConnection(ConnectionPool.java:25)
    at org.lkl.singleton.Test.main(Test.java:15)

     

     以上完成单例模式的笔记. 

  • 相关阅读:
    TP框架模板中IF Else 如何使用?
    Append 后如何使用 fadein淡入效果
    ThinkPad如何修改fn键默认操作
    TP框架ajax U方法不解析怎么办?
    thinkphp session如何取数组
    FTP服务搭建
    Linux系统学习之字符处理
    如何使用zabbix初级监控
    项目同步部署
    巡检常用命令
  • 原文地址:https://www.cnblogs.com/liaokailin/p/3627339.html
Copyright © 2011-2022 走看看