单例设计模式&&多例设计模式

单例模式
单例模式是 :一个类只允许产生一个实例化对象(如太阳类只有一个太阳对象)。
如何只产生一个是实例化对象?

  • 构造方法私有化: 我们知道是构造函数是实例化对象,要只产生一个实例化对象,就需要把构造方法用private封装起来,那么外部就无法调构造方法。
  • 在类里实例化一个私有全局静态对象:既然类外部无法实例化对象,就必须在类里实例化对象,而且对象需要是静态属性,如果不是静态,还得实例化对象取得这个属性,所以声明为静态,在类加载的时候就有这个对象。
  • 一个静态公有的取得实例化对象方法:既然在类里实例化的对象是私有,就要有一个getXX方法来取得这个对象,同样这个方法需要是静态,在外部通过类名来调用。
    单例模式又分为:饿汉模式和懒汉模式。
    饿汉模式是只要加载类就会new一个对象,而懒汉模式是用到对象才new。
    首先看饿汉模式代码:
package CODE;
//饿汉模式
class Person
{
    private  String name;
    private static final  Person person =new Person(); //实例化一个静态全局常量对象,
    // 因为这个对象实例化出来不会再被改变,所以声明为final ,static final 是全局常量
    private  Person() //构造方法私有化
    {}
    public  static Person getPerson()
    {
        return person;
    }
}
public class Singleton
{
    public static void main(String[] args)
    {
        Person person1=Person.getPerson();
        Person person2=Person.getPerson();
        Person person3=Person.getPerson();
        System.out.println(person1); //CODE.Person@4554617c
        System.out.println(person2); //CODE.Person@4554617c
        System.out.println(person3); //CODE.Person@4554617c
        //可以发现三个对象的地址是一样的,事实上只实例化了一个对象,三个引用指向同一块堆内部
    }
}

懒汉模式:

class Person
{
    private  Person() //构造方法私有化
    {}
    private static  Person person; //该成员变量默认值为null
    public  static Person getPerson()
    {
        if(person==null)  //懒汉模式
        {
            person=new Person();
        }
        return person;
    }
}

上述方法存在线程安全问题,可能会new 2个对象。

//懒汉:单例模式
class Singleton
{
    private static Singleton singleton;
    private Singleton()
    {

    }
    public static Singleton getSingleton()
    {
        if(singleton==null)
        {
            //多个线程进入同步块外面
            synchronized (Singleton.class)
            {
                if(singleton==null)
                {
                    //再次判断,是因为假如线程1、线程2都进入第一个if判断,线程1先进入同步代码块,
                    //new Singleton()后,释放锁后,线程2进入同步代码块,假如没有再次判断,会再次
                    //实例化对象
                    singleton=new Singleton();
                }
            }
        }
        return singleton;
    }
}
public class volatileSigton {
    public static void main(String[] args) {
        System.out.println(Singleton.getSingleton()==Singleton.getSingleton());
    }
}

但是上述代码有问题: singleton=new Singleton();此语句JVM会做3件事:1.给singleton分配内存,2.调用Singleton的构造函数来初始化成员变量,3.将singleton指向分配的内存空间(singleton!=null)。但是JVM的即时编译器中存在指令重排的优化,即第二步第三步顺序不能保证,假如是1、3、2执行:当第三步执行完毕,第二步没有执行,被线程2抢占,此时singleton!=null,但是没有初始化,线程2直接返回singleton,那么就存在问题。那么将singleton声明为volatile,即禁止指令重排,就OK。
那么完整单例模式如下:

//懒汉:单例模式
class Singleton
{
    private static volatile Singleton singleton;  //声明为volatile禁止指令重排
    private Singleton()
    {

    }
    public static Singleton getSingleton()
    {
        if(singleton==null)
        {
            synchronized (Singleton.class)
            {
                if(singleton==null)
                {
                    singleton=new Singleton();
                }
            }
        }
        return singleton;
    }
}
public class volatileSigton {
    public static void main(String[] args) {
        System.out.println(Singleton.getSingleton()==Singleton.getSingleton());
    }
}

多例模式:
多例模式指可以实例化出有限个对象。
多例和单例的区别是对象个数,那么在设计单例模式基础上需要加上以下步骤:
在类里定义有限个静态对象,用flag返回想要的对象,代码如下:

////多例模式
class Sex
{
    private String sex;
    public static final int MALE_FLAG=1;
    public static final int FEMALE_FLAG=2;
    private static final Sex male=new Sex("男"); 
    private static final Sex femal=new Sex("女");
    private Sex(String sex)
    {
        this.sex=sex;
    }
    public static Sex getSex(int flag)  //定义一个falg,告诉要什么类型对象
    {
       switch(flag)
       {
           case MALE_FLAG:
               return male;
           case FEMALE_FLAG:
               return femal;
           default:
               return null;
       }
    }
    public String toString()
    {
        return this.sex;
    }
}
public class Singleton
{
    public static void main(String[] args)
    {

        Sex male=Sex.getSex(Sex.MALE_FLAG);
        Sex female=Sex.getSex(Sex.FEMALE_FLAG);
        System.out.println(male);  //男
        System.out.println(female); //女
    }
}

其实多例模式我们只需要理解概念即可,多例模式已经被枚举取代。

单例设计模式的应用场景:
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。
1.资源共享时,避免由于资源操作时导致的性能或损耗,如日志文件、应用配置。
日志文件:共享的日志文件一直处于打开状态,只能有一个实例去操作,否则内容不好追加;
日志文件是后缀为.log的文件,是由系统自动记录的关于系统,应用程序,安全等方面的正常或异常事件。
数据库连接池:数据库连接池是一种共享资源,数据库,软件系统使用数据库连接池,主要是节省打开或关闭数据库的数据库连接所引起的性能损耗,用单例来维护,可以降低这种损耗。
回收站:在整个运行过程,回收站一直维护一个仅有的实例。
2.控制资源的情况,方便资源之间的相互通信,如线程池。线程池对池中线程方便控制。

你可能感兴趣的:(java,多例模式,单例模式)