1、饿汉式单例
代码:
public class Hungry {
//私有构造函数,保证其他类不能实例化此类
private Hungry(){
}
//自己创建一个类的实例化
private final static Hungry HUNGRY = new Hungry();
//提供公共的访问方式
public static Hungry getInstance(){
return HUNGRY;
}
}
但是这样的单例模式存在一定问题。
若是在这个类中有一些数组,而一上来就给他创建了实例,则会造成空间的浪费。
public class Hungry {
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private byte[] data4 = new byte[1024*1024];
//私有构造函数,保证其他类不能实例化此类
private Hungry(){
}
//自己创建一个类的实例化
private final static Hungry HUNGRY = new Hungry();
//提供公共的访问方式
public static Hungry getInstance(){
return HUNGRY;
}
}
2、懒汉式单例
public class LazyMan {
//私有构造函数,保证其他类不能实例化此类
private LazyMan(){
}
//自己创建一个类的实例化,并使用volatile关键字保证原子性
private volatile static LazyMan lazyMan;
//创建get方法返回一个实例
public static LazyMan getInstance(){
if (lazyMan == null){
synchronized (LazyMan.class){
if (lazyMan == null) {
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
}
这样的单例模式也是有一定问题的。若是没有volatile关键字:
由于lazyMan = new LazyMan();不是一个原子性操作,他会经过三步操作。
- 分配内存空间
- 执行构造函数,初始化对象
- 把这个对象指向这个空间
这三步没有顺序性,执行的步骤可能是1、2、3,也可能是1、3、2。
若是第一个线程执行1、3、2执行到3的时候,第二条线程进入,判断LazyMan不为空。直接返回,则会有其他问题。
3、静态内部类实现单例模式
public class Holder {
//构造器私有
private Holder(){
}
//提供get方法
public static Holder getInstance(){
return InnerClass.HOLDER;
}
//静态内部类
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
4、反射破解单例模式
4.1、代码
public static void main(String[] args) throws Exception {
//正常获取LazyMan对象
LazyMan instance = LazyMan.getInstance();
//通过反射获取LazyMan对象
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);//LazyMan@45ee12a7
System.out.println(instance2);//LazyMan@330bedb4
}
4.2、阻止反射破解单例模式
在无参构造中添加判断:
private LazyMan(){
synchronized (LazyMan.class){
if (lazyMan==null ){
throw new RuntimeException("请勿使用反射破坏单例模式");
}
}
}
5、使用枚举类型实现单例模式
写一个枚举类:
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
尝试用反射破解
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
EnumSingle instance = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance == instance2);
}
报错: