Singleton模式


当程序中需要类“只能创建一个实例”时,如表示字符串的 java.lang.String 类的实例与字符串,像这样的模式称为 Singleton模式(单例模式)

单例模式主要特点:

  • 想确保任何情况下都绝对只有1个实例
  • 想在程序上表现出“只存在一个实例”

示例程序


类的一览表:

名字说明
Singleton只存在一个实例的类
Main测试程序行为的类

示例程序的类图:

Singleton类

Singleton类只会生成一个实例。Singleton类定义了 static 字段(类的成员变量)singleton,并将其初始化为 Singleton 类的实例。初始化为Sigleton类的实例。初始化行为仅在该类被加载时进行一次。

public class Singleton {  
    private static final Singleton singleton = new Singleton();  
    private Singleton() {  
        System.out.println(" 生成了一个实例。 ");  
    }  
    public static Singleton getInstance() {  
        return singleton;  
    }  
}
  • Singleton类的构造函数是 private 的,这是为了禁止从 Singleton 类外部调用构造函数

Main类

public class Main {  
    public static void main(String[] args) {  
        System.out.println("Start.");  
        Singleton obj1 = Singleton.getInstance();  
        Singleton obj2 = Singleton.getInstance();  
        if (obj1 == obj2) {  
            System.out.println("obj1与obj2是相同的实例");  
        } else {  
            System.out.println("obj1与obj2是不同的实例");  
        }  
        System.out.println("End.");  
    }  
}

运行结果如下:

Start.
 生成了一个实例。 
obj1与obj2是相同的实例
End.

Singleton模式中的登场角色


Singleton

在 Singleton 模式中,只有 Singleton 这一个角色。Singleton角色有一个返回唯一示例 static 方法。该方法总是会返回同一个实例

Singleton 模式类图

拓展思路


为什么必须设置限制

当存在多个实例中,实例之间若是互相影响,可能产生意想不到的bug。
如果确保只有一个实例,那么就不需要担心实例间的影响了。

何时生成这个唯一的实例

当程序运行后,第一次调用 getInstance() 时。也是在这个时候,static 字段 singleton 被初始化,生成了唯一的一个实例。

练习题


习题1

在下面的TicketMaker类(代码清单5-3)中,每次调用getNextTicketNumber方法都会返回1000,1001,1002...的数列。我们可以用它生成票的编号或是其他序列号。在现在该类的实现方式下,我们可以生成多个该类的实例。请修改代码,运用Singleton模式确保只能生成一个该类的实例。

TicketMaker.java:

public class TicketMaker {  
    private int ticket = 1000;  
    public int getNextTicketNumber() {  
        return ticket++;  
    }
}

使用单例模式修改:

public class TicketMaker {  
    private static final TicketMaker ticketMaker = new TicketMaker();  
    private int ticket = 1000;  
    public int getNextTicketNumber() {  
        return ticket++;  
    }  
  
    public static TicketMaker getInstance() {  
        return ticketMaker;  
    }  
}

没使用示例的话,new出来的对象都不是同一个,因此getNextTicketNumber都会返回 1000,但是使用单例就可以累加。

习题2

请编写Triple类,实现最多只能生成3个Trip1e类的实例,实例编号分别为0,1,2且可以通过getInstance(int id)来获取该编号对应的实例(在第10章中也出现了这样的类)。

 public class Triple {  
  
    private static final Triple[] triples = new Triple[]{  
            new Triple(0),  
            new Triple(1),  
            new Triple(2)  
    };  
    private int id;  
  
    private Triple() {  
    }  
    private Triple(int id) {  
        System.out.println("The instance " + id + " is created.");  
        this.id = id;  
    }  
  
    public static Triple getInstance(int id) {  
        return triples[id];  
    }  
  
    @Override  
    public String toString() {  
        return "Triple id=" + id;  
    }  
}

Main

public class Main {  
    public static void main(String[] args) {  
        for (int i = 0; i < 9; i++) {  
            Triple triple = Triple.getInstance(i % 3);  
            System.out.println(i + ":" + triple);  
        }  
        System.out.println("End.");  
    }  
}