单例模式应该是23种设计模式中最简单的一种模式。它包含以下几个要素
1私有的构造方法
2指向自己实例的私有静态引用
3以自己实例为返回值的静态的公有的方法
单例模式根据实例化对象的不同时间分为饿汉和懒汉模式(其中还有一种登记式单例);
饿汉式单例在类被加载时候,实例化一个对象;而懒汉式在调用取得实例方法的时候才会实例化对象
单例有如下的优点:
在内存中只有一个对象。
避免频繁的创建销毁对象。
避免对共享资源的多重占用。
懒汉式单例:
public class Singleton1
{
private static Singleton1 instance = new Singleton1();
private Singleton1()
{
}
public static Singleton1 getInstance()
{
return instance;
}
}
在类第一次加载实例化一个实例,所以在使用时,花销的时间比较少,比较快。
饿汉式单例:
public class Singleton
{
private static Singleton instance = null;
private Singleton()
{
}
public synchronized static Singleton getInstance()
{
if(null == instance)
{
instance = new Singleton();
}
return instance;
}
}
该单例方式,只有在第一次调用getInstance()时才会初始化一个实例,所以在第一次使用时会花销一定的时间,同时需要同步,同样会花销一定的时间,这样性能上会比第一种要差点,下面我们会一一的验证。
首先我们验证一下是否通过单例提供的方法产生的实例是不是就是一个实例?
Singleton1 instance1 = Singleton1.getInstance();
instance1.setId("name");
Singleton1 instance2 = Singleton1.getInstance();
System.out.println(instance2.getId());
我们通过getInstance()获取实例,在第一个实例中设置了一个属性值,在获取一个实例,输出他的id的值,
name
我们并没有设置第二个实例属性值,但是他的id值已经被设置为name,这一点说明我们获得是同一个实例。
这里有一个需要注意的问题,通过反射每次都能获得类的新的实例,所以如果你想使用单例,就不要使用反射创建类的实例对象。
通过下面的例子我们可以验证一下
Singleton1 instance1 = Singleton1.getInstance();
instance1.setId("name");
Singleton1 instance2 = Singleton1.getInstance();
System.out.println(instance2.getId());
Class c = Class.forName(Singleton1.class.getName());
Constructor ct = c.getDeclaredConstructor();
ct.setAccessible(true);
Singleton1 instance3 = (Singleton1)ct.newInstance();
System.out.println(instance3.getId());
我们可以试想下结果应该是什么(上面我们说明)
name
null
上面我们还提到两种单例的性能问题,下面我们在用例子说明一下:
//饿汉
Long startTime1 = System.currentTimeMillis();
for(int i = 0 ;i<100000000;i++)
{
Singleton1 instance = Singleton1.getInstance();
}
Long endTime1 = System.currentTimeMillis();
System.out.println(endTime1 - startTime1);
//懒汉
Long startTime = System.currentTimeMillis();
for(int i = 0 ;i<100000000;i++)
{
Singleton instance = Singleton.getInstance();
}
Long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
通过多次执行我们可以看一下结果如何:这里我们执行了三次
171 187 187
5398 5398 5429
可以明显的看出饿汉比懒汉式单例性能上好,所以你使用了单例,如果在乎性能的话,请使用饿汉式。