Java设计模式之单例模式(Singleton pattern)
Java设计模式之单例模式(Singleton Pattern)
[导读]
目前Java的设计模式(Design Pattern)有23种,设计模式作为提高代码的复用性并解决同一类问题的科学工具,就如同东方人使用筷子吃饭,西方人使用刀叉吃西餐一样,这里的筷子、刀叉便是一种生活中的“设计模式”,当我们吃东西的时候不必过多考虑,拿起筷子撸就可以了。Java的设计模式也是基于一些共性问题做了代码优化总结,以便于开发者的使用。
本文就设计模式之一的单例模式(Singleton Pattern)进行原理分析。
[提出问题]
我们想象这样一个问题,假设现在我们需要用Java来记录太阳的姓名和年龄,并且可以查询和修改这两个变量,如何实现?
一般来说,我们需要创建一个“太阳”类,来封装这个对象并对其成员变量进行操作:(代码如下)
1 public class Sun { 2 private String name; 3 private int age; 4 5 public String getName() { 6 return name; 7 } 8 public void setName(String name) { 9 this.name = name; 10 } 11 public int getAge() { 12 return age; 13 } 14 public void setAge(int age) { 15 this.age = age; 16 } 17 }
但是我们会发现一个问题,实际操作中我们可以无限的进行new sun();操作,这表示这个太阳不止一个,且每个太阳的姓名和年龄都可以不一样,但我们的需求是“只有一个太阳,只对一个太阳进行操作”。又该如何解决呢?
[解决“太阳不唯一”的问题]
学过static的童鞋应该都想到了可以将变量静态化,这样做确实可以让数据共享从而保证唯一性。但任然可以无限进行new sun();操作。另一个问题是如果变量不止两个,而是超级多,我们就需要对每个变量都静态化,这样就会产生各种各样的问题。所有,人们为了解决:保证一个类只有一个实例对象的问题,而设计出了单例模式。
[单例模式の原理]
接着上面“唯一的太阳”的问题,我们来具体分析一下该如何解决:
思路:
既然要保证一个类只能有一个实例对象,那么怎么才能保证对象唯一呢?
1、首先外界不能使用new来创建对象,不然就不能保证只有一个对象。
2、无法使用new,所以需要类本身自己创建好这个唯一的对象。
3、也需要对外提供调用该对象方法。
实现步骤:
1、不让外界创建对象,即不能进行new操作,而new操作的是类的构造函数,所以让构造函数私有化即可。
2、在类内部new一个该类型的对象最为唯一的实例对象。
3、对外暴露一个调用对象的方法,返回类型是该类类型。
4、值得注意的一个问题是:因为我们已经无法对该类进行new操作,所以只能让方法和对象静态化,用类名.方法名来直接调用。
具体代码如下:
1 public class Sun { 2 private String name; 3 private int age; 4 5 //步骤2:在本类中new这个唯一的实例对象(步骤4:静态化) 6 private static Sun instance = new Sun(); 7 8 //步骤1:构造函数私有化 9 private Sun(){ 10 11 } 12 13 //步骤3:对外暴露方法(步骤4:静态化) 14 public static Sun getInstance(){ 15 return instance; 16 } 17 18 public String getName() { 19 return name; 20 } 21 public void setName(String name) { 22 this.name = name; 23 } 24 public int getAge() { 25 return age; 26 } 27 public void setAge(int age) { 28 this.age = age; 29 } 30 }
这就是单例模式的设计思路,也是一种单例模式写法:我们把按照这种思路的写法称之为“饿汉式”。另外还有 “懒汉式”等写法,但都大同小异,建议使用“饿汉式”即可,因为“懒汉式”在 多线程中存在安全隐患。
[测试一下]
我们对刚才创建的“唯一的太阳”(即用单例模式设计的类sun)进行测试,看看是不是真的唯一?
1 public class SunTest { 2 3 public static void main(String[] args) { 4 5 Sun s1 = Sun.getInstance(); 6 Sun s2 = Sun.getInstance(); 7 s1.setName("日"); //嘿嘿(*^▽^*) 8 s2.setAge(11); 9 System.out.println("两个Sun引用是否指向同一个对象呢?"); 10 System.out.println(s1.equals(s2)); 11 12 System.out.println("s2调用了s1设置的sun的name:"+s1.equals(s2)); 13 System.out.println("s1调用了s2设置的sun的age:"+s1.equals(s2)); 14 } 15 16 }
运行结果:

结果很给力,我们实现了这个“唯一的太阳”。^_^
[一些细节]
单例模式的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。
为什么我们要设置成语变量私有化(Private)呢?我们一般希望通过get和set来获取和设置成员变量,以达到对数据的可控性。所以在开发过程中一般情况下我们都应该默认私有化成员变量,这样有助于控制数据,如上文的类Sun:如果我们有一个需求是:输入的年龄不能为负数。这样我们就可以修改setAge()方法:
1 private int setAge(int age){ 2 if(age >= 0) 3 return this.age = age; 4 }
这就是对数据的具备可控性的一个小栗子。
浙公网安备 33010602011771号