单例模式
单例模式是指,确保一个类在任何情况下都只有一个实例,供全局访问。
单例模式可以根据类加载的时机不同,可以分为饿汉式和懒汉式。
饿汉式
饿汉式单例模式在类加载阶段就开始初始化,并创建单例对象。由于单例对象的初始化发生在类加载阶段,所以没有因多线程并发造成的重复创建单例对象的问题。
优点:没有加任何锁,执行效率比较高,用户体验比较好,随用随取。
缺点:类加载的时候就实例化了,即便不使用它,它也在那占用内存空间。
饿汉式的两种写法
第一种写法:给静态字段直接赋值
public class HungrySingleton1 implements HungrySingleton {
private static HungrySingleton1 hungrySingleton1 = new HungrySingleton1();
private HungrySingleton1(){ }
public static HungrySingleton getInstance(){
return hungrySingleton1;
}
}
第二种写法:静态代码块
public class HungrySingleton2 implements HungrySingleton {
private static HungrySingleton2 hungrySingleton2;
static {
hungrySingleton2 = new HungrySingleton2();
}
private HungrySingleton2(){
}
public static HungrySingleton2 getInstance(){
return hungrySingleton2;
}
}
第二种写法与第一种写法相比,第二种方式可以在对象实例化前后做一些其他处理,适用于对象的创建过程比较复杂的情况。
懒汉式
懒汉式单例模式的特点是:在第一次通过类的静态方法获取该类的实例对象时,该类才会被实例化。
优点:使用该类的单例对象前不占用内存,第一次使用该单例对象后才开始占用内存。
缺点:需要加锁,否则当多个线程并发获取该类的实例对象时可能会创建两以上个实例对象。
public class LazySingleton {
private static LazySingleton lazySingleton;
private LazySingleton(){
}
public static LazySingleton getInstance(){
if(lazySingleton==null){
synchronized (LazySingleton.class){
lazySingleton = new LazySingleton();
}
}
return lazySingleton;
}
}
静态内部类方式
静态内部类方式是实现懒汉式的方式之一,它巧妙地利用jvm对静态内部类的加载机制,避免了给程序加锁,同时又保证了程序的安全性。
当jvm启动时会先加载外部类的静态字段并赋值,但并不加载内部类,当调用内部类的静态字段和方法时才加载内部类。
优点:懒汉式加载,程序不用加锁,不会因为多线程并发而创造多个单例对象。
public class InnerClassSingleton {
public static InnerClassSingleton getIntance(){
return InnerClass.instance;
}
private static class InnerClass{
private static InnerClassSingleton instance= new InnerClassSingleton();
}
}
防止反射破坏单例模式
上面的所有方法中,对于构造方法我们只是将他私有化,其他什么都没有做,这种情况下我们可以利用反射调用其构造方法,从而破坏单例模式。
测试代码:
public class Test3 {
public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
InnerClassSingleton intance1 = InnerClassSingleton.getIntance();
Class<InnerClassSingleton> clazz = InnerClassSingleton.class;
Constructor<InnerClassSingleton> declaredConstructor = clazz.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
InnerClassSingleton instance2 = declaredConstructor.newInstance();
System.out.println(intance1);
System.out.println(instance2);
}
}
解决反射破坏单例模式
只需要在构造方法里添加一个简单的判断,当重复创建单例对象就让它抛出异常。
public class InnerClassSingleton {
private InnerClassSingleton(){
if(InnerClass.instance!=null){
throw new RuntimeException("不允许重复创建InnerClassSingletion对象");
}
}
public static InnerClassSingleton getIntance(){
return InnerClass.instance;
}
private static class InnerClass{
private static InnerClassSingleton instance= new InnerClassSingleton();
}
}
再次测试: