今天我们来整理一下在Java中比较常见的单例设计与多例设计。
单例设计与多例设计主要是一种控制实例化对象产生个数的设计操作。
1.单例设计
首先让我们来看一段代码:
class Singleton{
public void print() {
System.out.println("www.csdn.com");
}
}
这是一个很简单的类,那么请问我们能够实现多少个对象?答案是:无数个。这里我们先实例化三个来看一下:
public class first {
public static void main( String args[] ){
Singleton singA = new Singleton();
Singleton singB = new Singleton();
Singleton singC = new Singleton();
singA.print();
singB.print();
singC.print();
}
}
运行上述代码,结果如下:
www.csdn.com
www.csdn.com
www.csdn.com
可以看到,这是一个非常简单的代码,但是现在我们要求Singleton这个类只允许提供一个实例化对象,那么此时我们首先应该控制的是哪里?应该是构造方法,因为所有的新的实例化对象产生都要使用构造方法,如果构造方法“没有了”,自然就无法产生实例化对象了。
于是我们将代码中的Singleton类的构造方法私有化,然后看一下哪里会出错:
class Singleton{
private Singleton() {} //构造方法私有化
public void print() {
System.out.println("www.csdn.com");
}
}
public class first {
public static void main( String args[] ){
Singleton instance = null; // 声明对象
instance = new Singleton(); //实例化对象
}
}
编译之后运行结果如下:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The constructor Singleton() is not visible
这时我们就可以发现:声明对象并没有错误,实例化对象反而出错了。但是现在是有一个要求是要产生一个实例化对象,而将构造方法私有化之后就不能实例化对象了,所以我们怎样将外部的对象实例化呢?现在我们就要想办法产生一个实例化对象交给客户端去调用,这个时候分析一下:
private 访问权限的特点是不能在类外部访问,但是可以在类本身调用,所以现在可以考虑在类的内部调用对象:
class Singleton{
private Singleton instance = new Singleton();
private Singleton() {} //构造方法私有化
public void print() {
System.out.println("www.csdn.com");
}
}
此时Singleton类内部的instance属于一个普通属性,而普通属性是在实例化对象产生之后才会被调用的,那么这时外部无法产生实例化对象,那么这个属性就不能访问到了,那么就必须考虑在如何在没有实例化对象的时候获取此属性,这是只有static属性可以访问。我们来看一下:
class Singleton{
static Singleton instance = new Singleton();
private Singleton() {} //构造方法私有化
public void print() {
System.out.println("www.csdn.com");
}
}
public class first {
public static void main( String args[] ){
Singleton instance = null; // 声明对象
instance = Singleton.instance;
instance.print();
}
}
编译没有问题,运行结果如下:
www.csdn.com
可以看到,我们为了能从外部访问将private删除了。类中的属性应该封装后再使用,所以此时的instance应该进行封装处理:
class Singleton{
static Singleton instance = new Singleton();
private Singleton() {} //构造方法私有化
public static Singleton getInstance() {
return instance;
}
public void print() {
System.out.println("www.csdn.com");
}
}
public class first {
public static void main( String args[] ){
Singleton instance = null; // 声明对象
instance = Singleton.getInstance();
instance.print();
}
}
这样子就没有问题了,但是整个代码从头到尾都在强调只有一个实例化对象,这个时候虽然提供有一个static实例化对象,但是这个对象应该可以被重新实例化:
class Singleton{
static Singleton instance = new Singleton();
private Singleton() {} //构造方法私有化
public static Singleton getInstance() {
instance = new Singleton(); //如果我们添加这行代码
return instance;
}
public void print() {
System.out.println("www.csdn.com");
}
}
如果我们添加这行代码,这个程序依然是可以被调用的,这就表示每获得一次都是在实例化一个新的对象,此时需要保证Singleton内部的instance无法再次实例化,应该使用final定义:
class Singleton{
private static final Singleton INSTANCE = new Singleton();
private Singleton() {} //构造方法私有化
public static Singleton getInstance() {
return INSTANCE;
}
public void print() {
System.out.println("www.csdn.com");
}
}
这就实现了一个单例的设计模式。
那为什么我们要一直强调单例设计模式呢?因为在很多情况下,有些类是不需要重复产生对象的,比如说:一个程序启动后,会有一个类来收集启动信息,那么就没有必要每次启动都实例化一个对象来收集信息,只需要有一个对象一直存在就可以了。
这个例子在我们的电脑上有一个很好的体现:回收站。在电脑上打开不同的磁盘,不同的文件夹,会发现不同位置删除文件后到的回收站是同一个,这就说明了在不同位置使用的回收站一直是同一个,而不是每次删除都会使用一个新的回收站。
同时对单例设计模式也有两种设计模式:懒汉式和饿汉式。
在之前我们的例子都是饿汉式单例模式,在系统加载类的时候就会自动提供我们的Singleton对象;而懒汉式就是,在第一次使用时进行实例化对象处理:
class Singleton{
private static Singleton instance;
private Singleton() {} //构造方法私有化
public static Singleton getInstance() {
if(instance == null) { //如果是第一次使用
instance = new Singleton();
}
return instance;
}
public void print() {
System.out.println("www.csdn.com");
}
}
这就是懒汉式单例模式,如果是第一次使用程序,那么就会实例化一个对象,而后就再也不实例化。
如果我们在面试的是时候需要介绍Singleton程序,并且说明单例模式的特点,那我们就要进行下面的说明:
- 首先将懒汉式与饿汉式的程序都写出来,同时说明两种的思路,注意懒汉式以后要考虑线程问题
- 特点:构造方法私有化,内部类提供static方法获取实例化对象,这样不管外部如何操作永远都只有一个实例化对象提供。
这就是单例模式的特点,在以后使用Spring框架的时候经常会看到单例设计模式。
2.多例设计模式
与单例模式对应的还有一个多例设计模式,单例设计只保留有一个实例化对象,而多例设计模式指的是可以保留有多个实例化对象,例如:定义一个描述性别的类,那么只有两个对象男和女;定义一个描述三原色的类,那么对象只有红绿蓝三个等。在这种情况下我们就可以使用多例设计模式。
我们以刚才举得颜色类为例:
class Color{
private static final Color RED = new Color("红色");
private static final Color GREEN = new Color("绿色");
private static final Color BLUE = new Color("蓝色");
private String title;
private Color(String title) {//构造方法私有化
this.title = title;
}
public static Color getInstance(String color) {
switch(color) {
case "red" :return RED;
case "green" :return GREEN;
case "blue" :return BLUE;
default :return null;
}
}
public String toString() {
return this.title;
}
}
public class first {
public static void main( String args[] ){
Color color = Color.getInstance("green");
System.out.println(color);
}
}
这就是一个多例设计,运行结果如下:
绿色
多例设计与单例设计的本质是相同的,一定会有在内部提供static方法获取实例化对象。从整体设计来件,单例设计模式会比多利设计模式整体使用多一些,我们需要注意的是:只要不是public都算私有化。
单例设计模式与多例设计模式都是比较实用的设计,以后的框架代码中会经常看到他们的身影,我们下次见👋