以前用singleton的时候就接触过饿汉懒汉两种
最近书上看到一个对于我来说是新的方法,感觉还是很不错的
这里将三种方法放一起,比较总结一下
一、饿汉式
package com.aii.algorithm;
public class HungrySingleton {
private HungrySingleton() {
}
private static HungrySingleton instance = new HungrySingleton();
public static HungrySingleton getInstance() {
return instance;
}
}
缺点:
该类有一个静态的常量instance,这个instance实例在这个类加载到内存的时候就已经初始化。
类什么时候被加载进内存:当然是第一次使用到的时候,如果这个类里还有其他静态方法,如
public static void print(){
System.out.println("1111");
}
那么当调用HungrySingleton.print()的时候,就自动生成了一个instance对象,如果这个对象又很大,那么留在内存中影响性能。
使用懒加载的方式可以避免过早实例化。
二、懒汉式
<pre name="code" class="java" style="font-size: 13.3333339691162px;">package com.aii.algorithm;
public class LazySingleton {
private LazySingleton() {
};
private static LazySingleton instance = null;
public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
懒汉式有一个优点,那就是懒加载。
懒加载有什么好处:
当前类如果实例化对象会非常大,调用他的静态方法的时候,根本没有必要初始化instance,这个时候使用饿汉式会多出这个大对象,而懒汉式不会。
缺点:
具体什么时候实例化,得看调用而定,所以在初始化的时候得做判断。
但是多线程的环境下,又能产生多个对象,所以得加锁。
但是每次判断前都加锁,每次调用都先进锁,难免影响性能。
所以使用双重判断。
所以缺点就是程序写起来麻烦。。。。
三、内部类
<pre name="code" class="java">package com.aii.algorithm;
public class InnerSingleton {
private static class Inner {
private static InnerSingleton instance = new InnerSingleton();
static InnerSingleton get() {
return instance;
}
}
public static InnerSingleton getInstance() {
return Inner.get();
}
private InnerSingleton() {
}
}
这个方法非常巧妙:
把类的实例放到内部类中:
什么时候实例化呢?只有当内部类被加载到内存的时候。
内部类什么时候被加载到内存?只要我这个类不调用你,永远也不会进内存(外面的类访问不到)
四、真正的单例
学习了反射后,发现上面的单例都是可破解的,很容易能够弄到第二个对象。
那么怎么样才能真正做到单例呢?
Unsafe这个类给了我一些启发,因为在我用反射直接去直接获取他的对象的时候会报错。
然后可以考虑一下下面的方法。
package com.aii.algorithm;
public class HungrySingleton {
private static boolean flag = false;
private HungrySingleton() {
if (flag) {
throw new RuntimeException();
}
}
private static HungrySingleton instance = newInstance();
public static HungrySingleton getInstance() {
return instance;
}
private static HungrySingleton newInstance() {
HungrySingleton instance = new HungrySingleton();
flag = true;
return instance;
}
}
写到这里发现,好像还是可以通过反射改变flag的值的,,,不过就当是加了一层保障把。。。
五、用法
平常用的话,如果一定要单例,简单起见,用饿汉式即可。
如果这个对象非常大,并且经常调用他的静态方法而不使用他的对象,那么可以考虑使用懒汉式或者内部类,毕竟写着麻烦写一次就够了。
在javaWeb开发中,基本用不到这样的单例,在spring,对象默认就是单例的,我们也不用这些方法去限制他只能产生一个对象,单例靠容器来控制,我们基本不手动产生对象。