单例模式介绍:
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,**可以直接访问,不需要实例化该类的对象。**其实就是无论你调多少这对象,他就是同一个。
例如,我们在系统的多个地方需要读取一个配置文件,我们并不需要每次都去new一个实例,然后去读文件,只需要维护一个全局的Config类,并且每次使用的时候校验下文件是否变更即可。依赖可以减少类的创建跟销毁的时候的开销,二来也减少了读取文件的次数。就像spring的ioc容器。默认就是单例,节省资源, @Autowired进来的就是单例,再写service层时注意不要把mapper也注入,因为mapper是需要改的。
注意:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例,也就是给一个静态方法,类调用。
懒汉式
懒加载,就是神魔时候用,我就神魔时候加载,在方法调用时创建,会被反射创建多个实例。
分为线程安全和不安全,差距就是在外部调用方法加synchronized
还有就是下面的双检锁/双重校验锁,其实就是看看原来有没有创建过实例,创建过就不创建了,相对来说哦比较好。
因为创建对象时有赋值,调用构造操作,线程不安全。
// 懒汉式单例
// 道高一尺,魔高一丈!
public class LazyMan {
private static boolean qinjiang = false;
private LazyMan(){
synchronized (LazyMan.class){
if (qinjiang == false){
qinjiang = true;
}else {
throw new RuntimeException("不要试图使用反射破坏异常");
}
}
}
private volatile static LazyMan lazyMan;
// 双重检测锁模式的 懒汉式单例 DCL懒汉式
public static LazyMan getInstance(){
if (lazyMan==null){
synchronized (LazyMan.class){
if (lazyMan==null){
lazyMan = new LazyMan(); // 不是一个原子性操作
}
}
}
return lazyMan;
}
// 反射!
public static void main(String[] args) throws Exception {
// LazyMan instance = LazyMan.getInstance();
Field qinjiang = LazyMan.class.getDeclaredField("qinjiang");
qinjiang.setAccessible(true);
Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
LazyMan instance = declaredConstructor.newInstance();
qinjiang.set(instance,false);
LazyMan instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
饿汉式:
顾名思义,就是固定的对象数,类一加载就会创建固定对象,用的时候调方法取就行,
线程安全,没有枷锁,性能较快,就是是通过类加载包政的安全,会导致空间的浪费
// 饿汉式单例
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;
}
}
静态内部类:
这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。
想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。
// 静态内部类
public class Holder {
private Holder(){
}
public static Holder getInstace(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
枚举型:
防止反序列化重新创建新的对象,绝对防止多次实例化。最流批,不怕反射!
让JVM来帮我们保证线程安全和单一实例的问题,超丝滑
// enum 是一个什么? 本身也是一个Class类
public enum EnumSingle {
INSTANCE;
public void doSomething(){
//逻辑
}
}
public class Main {
public static void main(String[] args) {
Singleton.INSTANCE.doSomething();
}
}