JAVA23种设计模式四——单例模式
单例模式是一种常用的软件设计模式,在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
什么是单例模式?
一个类有且仅有一个实例,并且自行实例化向整个系统提供。
也就是说,在整个程序空间中,该类只存在一个实例对象。
GoF对单例模式的定义:
保证一个类,只有一个实例存在,同时提供能对该实例加以访问的全局访问方法。
为什么要使用单例模式?
对于系统中的某些类来说,只有一个实例很重要。比如:一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。在多线程之间,比如servlet环境,共享一个资源或者操作同一个对象,在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例模式。
单例模式的三个要点:
一是某个类只能有一个实例;
二是它必须自行创建这个实例;
三是它必须自行向整个系统提供这个实例。
具体一点就是:
一是单例模式的类只提供私有的构造函数;
二是类定义中含有一个该类的静态私有对象;
三是该类提供了一个静态的共有的函数用于创建或获取它本身的静态私有对象。
一般Singleton模式通常有三种形式:
第一种:饿汉式
// MainClass.java
public class MainClass {
public static void main(String[] args)
{
Person per=Person.getPerson();
Person per2=Person.getPerson();
per.setName("张三");
per2.setName("李四");
System.out.println(per.getName());
System.out.println(per2.getName());
}
}
// Person.java
public class Person {
public static final Person person=new Person();
// 保证该类里只有一个实例
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 构造函数私有化
private Person()
{
}
// 提供一个全局的静态方法
public static Person getPerson()
{
return person;
}
}
第二种:懒汉式
// MainClass.java
public class MainClass {
public static void main(String[] args)
{
Person2 per=Person2.getPerson();
Person2 per2=Person2.getPerson();
per.setName("张三");
per2.setName("李四");
System.out.println(per.getName());
System.out.println(per2.getName());
}
}
// Person2.java
public class Person2 {
// 保证该类里只有一个实例
private String name;
// 声明一个静态的person
private static Person2 person;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 构造函数私有化
private Person2()
{
}
// 提供一个全局的静态方法
public static Person2 getPerson()
{
// 在这里需要加一个判断,否则在主方法里会产生两个不同的对象
if(person==null)
{
person=new Person2();
}
return person;
}
}
结果还是:李四
李四
在上面Person2.java代码中,适用于单线程的情况,不适用多线程,要想适用多线程,需将相应代码修改为:
// 提供一个全局的静态方法,使用同步方法
public static synchronized Person2 getPerson()
{
// 在这里需要加一个判断,否则在主方法里会产生两个不同的对象
if(person==null)
{
person=new Person2();
}
return person;
}
这种做法又致使效率大大降低,因为每次获取对象时,都要进行同步!
第三种:双重检查
// MainClass.java
public class MainClass {
public static void main(String[] args)
{
Person3 per=Person3.getPerson();
Person3 per2=Person3.getPerson();
per.setName("张三");
per2.setName("李四");
System.out.println(per.getName());
System.out.println(per2.getName());
}
}
// Person3.java
public class Person3 {
// 保证该类里只有一个实例
private String name;
// 声明一个静态的person
private static Person3 person;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 构造函数私有化
private Person3()
{
}
// 提供一个全局的静态方法
public static Person3 getPerson()
{
// 双重检查,效率大大提高
if(person==null)
{
synchronized (Person3.class)
{
if(person==null)
{
person=new Person3();
}
}
}
return person;
}
}
这种模式大大提高了执行效率,不必每次获取对象时都进行同步,只是第一次需要进行同步。