单例模式
效果
- 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
- 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
分类
常见实现方式
- 饿汉式:线程安全,调用效率高,不能延时加载。
- 懒汉式:线程安全,调用效率不高,可延时加载。
其他实现方式
- 双重检测锁式:由于JVM底层内部模型原因,偶尔出问题,不建议使用。
- 静态内部类式:线程安全,调用效率高,可延时加载。
- 枚举单例:线程安全,调用效率高,不能延时加载。
选择方法
- 单例对象占用资源少,不需要延时加载:枚举式好于饿汉式;
- 单例对象占用资源多,需要延时加载:静态内部类好于懒汉式。
代码
//饿汉式单例
public class Hungry {
private Hungry() {
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance() {
return HUNGRY;
}
}
//懒汉式单例
public class LazyMan {
//添加标志
private static boolean tolaris = false;
private LazyMan() {
//标志判断
synchronized (LazyMan.class) {
if (tolaris == false) {
tolaris = true;
} else {
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
// synchronized (LazyMan.class) {
// if (lazyMan != null) {
// throw new RuntimeException("不要试图使用反射破坏异常");
// }
// }
}
private volatile static LazyMan lazyMan;
//双重检测锁模式
private static LazyMan getInstance() {
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();//不是原子性操作
}
}
}
return lazyMan;
}
//通过反射破坏
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
//通过正常的创建
//LazyMan instance1 = LazyMan.getInstance();
//获取我们设置的标志字段
Field tolaris = LazyMan.class.getDeclaredField("tolaris");
tolaris.setAccessible(true);
//获取构造器
Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
//无视私有的构造器
constructor.setAccessible(true);
//通过反射来创建两个对象
LazyMan instance1 = constructor.newInstance();
//将第一个对象的值改为false
tolaris.set(instance1, false);
LazyMan instance2 = constructor.newInstance();
System.out.println(instance1.hashCode());
System.out.println(instance2.hashCode());
}
}
//静态内部类
public class Holder {
private Holder() {
}
public static Holder getInstance() {
return InnerClass.HOLDER;
}
public static class InnerClass {
private static final Holder HOLDER = new Holder();
}
}
//枚举
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance() {
return INSTANCE;
}
}
class test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
EnumSingle instance1 = EnumSingle.INSTANCE;
//默认不是无参构造器
Constructor<EnumSingle> constructor =
EnumSingle.class.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
EnumSingle instance2 = constructor.newInstance();
System.out.println(instance1);
System.out.println(instance2);
}
}