1.饿汉模式
public class Singleton2 {
private static final Singleton2 singleton = new Singleton2();
private Singleton2() {}
public static Singleton2 getInstance() {
return singleton;
}
}
在类加载时初始化单例,得益于类加载机制,饿汉模式线程安全,使用时没有延时。
坏处是有可能造成资源浪费(如果类加载后就一直不使用单例的话)。
值得注意的是,单线程环境下,饿汉与懒汉在性能上没有什么差别;但在多线程环境下,由于懒汉需要加锁,饿汉的性能反而更优。
2.用静态内部类实现的单例模式(Holder模式)
public class Singleton {
private static LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {};
public static Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
//获得构造器
Constructor con = Singleton.class.getDeclaredConstructor();
//设置为可访问
con.setAccessible(true);
//创建两个不同的对象
Singleton Singleton1 = (Singleton) con.newInstance();
Singleton Singleton2 = (Singleton) con.newInstance();
3.利用枚举防止反射的构建方式
public enum SingletonEnum {
INSTANCE;
}
缺点:其单例对象是在枚举类被加载时完成初始化的。
枚举实现单例模式的本质:
public class Singleton4 extends Enum<Singleton4> {
...
public static final Singleton4 SINGLETON = new Singleton4();
...
}
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
几点补充:
1. volatile关键字不但可以防止指令重排,也可以保证线程访问的变量值是主内存中的最新值。有关volatile的详细原理,我在以后的漫画中会专门讲解。
2.使用枚举实现的单例模式,不但可以防止利用反射强行构建单例对象,而且可以在枚举类对象被反序列化的时候,保证反序列的返回结果是同一对象。
对于其他方式实现的单例模式,如果既想要做到可序列化,又想要反序列化为同一对象,则必须实现readResolve方法。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(来自程序员小灰)