Java单例模式,看这一篇就够了

在创建型设计模式中,我们第一个学习的是单例模式(Singleton Pattern) , 这是设计模式中最简单的模式之一 。
单例是什么意思呢?
单例就是单实例的意思,即在系统全局,一个类只创建一个对象 , 并且在系统全局都可以访问这个对象而不用重新创建 。
一、单例模式的基本写法单例模式示例代码:
public class Singleton {// Singleton类自己持有这个单例对象private static Singleton instance = new Singleton();// 构造方法设置为私有 , 避免在Singleton类外部创建Singleton对象private Singleton() {}// 提供获取单例对象的静态方法public static Singleton getInstance() {return instance;}public void hello() {System.out.println("Hello!");}}使用:
Singleton obj = Singleton.getInstance();obj.hello();分析SingleObject类的特征:

  1. SingleObject类的构造方法是私有的,这样可以保证只能在SingleObject类内部才能创建对象,而无法在类外部创建SingleObject对象 。
  2. SingleObject类中有一个instance成员属性,它用来持有这个SingleObject对象 。
  3. SingleObject类提供了一个静态方法getInstance,它可以让我们在任何可以访问到SingleObject类的地方,都可以使用SingleObject.getInstance()来获取到这个SingleObject对象 。
二、单例模式的作用单例模式有什么用呢?
1. 控制对象的数量
当你编写了一个类提供给其他人调用时,对方看到是一个类 , 很有可能第一反应是尝试new一下 。
你自己编写的类你自己是清楚如何使用的,在整个系统内这个类只需要创建一个对象就够了,但对方可能并不清楚 。
这时候你可以把这个类编写为单例形式,把构造方法私有化,让对方无法通过new来创建对象,只能使用getInstance来获取 。
这个模式可以帮助你有效的控制对象的数量,毕竟 , 有的类其内部实现复杂,如果频繁创建销毁对象,可能还是很耗费服务器资源的 。
2.全局访问
单例模式的特点是单例类自己持有这个单例对象 , 并且提供一个静态方法可在全局获取到这个单例对象 。
如果没有单例模式的情况下 , 我们一般是在代码A处创建这个对象,在代码B处如果也要使用这个对象,就需要将这个对象进行参数传递 。为了避免传来传去,我们可能会写个Holder类,把这个对象放在Holder的成员变量中 。
而单例模式的这个优点是,我们可以避免这样的困扰,直接从单例类中获取 。
三、单例模式的变种上面介绍的是单例模式的一种基本写法,实际我们还可以对其进行优化和变种 。
1. 饿汉式基本写法中,对象的创建是直接写在Singleton类的成员属性上的,因此当Singleton类被加载时,就会立即创建Singleton对象,这个写法比较简单,但我们可能并不会马上使用到这个Singleton对象,过早的创建会造成内存资源浪费 。
这种一加载类就急于创建对象的写法 , 我们称之为饿汉式 。
如果对内存资源不在意,那么其实饿汉式这个写法也就没什么大的缺点,而且写起来还简单,还是可以用的 。
2. 懒汉式(线程不安全)此变种仅是介绍,不要使用 。
既然饿汉式在类加载时就创建对象会造成内存浪费,那么我们把创建对象这个步骤挪到要用时再创建不就好了?
我们要使用对象时 , 都是通过getInstance方法先获取对象,我们可以在getInstance方法中完成对象创建 。
这种需要时再创建的写法,我们称之为懒汉式
示例代码:
public class Singleton {private static Singleton instance;private Singleton () {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}}分析懒汉式(线程不安全)写法的特点:
  1. 创建对象的时机修改为了在getInstance内部,需要时再创建,这可以节约系统资源
  2. getInstance方法在多个线程并发调用时,有可能会出现创建了多个实例,所以这算是一个不好的单例变种示范
饿汉式没有多线程并发问题吗?
确实没有,因为饿汉式是在类加载时进行创建对象,类加载classloader是单线程的,不存在这个问题 。
3. 懒汉式(线程安全)此变种仅是介绍,不要使用 。
懒汉式(线程不安全)有可能存在并发问题 , 导致创建多个实例 , 那么我们给他加上锁不就好了吗?
示例代码:
public class Singleton {private static Singleton instance;private Singleton () {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}}

推荐阅读