1.单例模式常用方法及反射序列化测试

1.单例模式

单例模式,顾名思义就是只有一个实例,并且她自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。下面我们来看下有哪几种实现方式吧。

1.1懒汉模式

​ 实例在用到的时候才去创建,“比较懒”,用的时候才去检查有没有实例,如果有则返回,没有则新建。

缺点 : 多线程访问时,会出现2个对象(线程不安全)

/**
 * TODO(描述)
 *  懒汉模式
 * @author vioelt
 * @Title LazySingleton
 * @Description:
 * @date 2020/6/28 8:51
 */
public class LazySingleton {
    private  static LazySingleton instance;
    public LazySingleton() {
    }
    public  static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}
1.2懒汉双检锁**(线程安全)**

虽然保证了线程安全,但是比较消耗资源

/**
 * TODO(描述)
 *  懒汉双检锁
 * @author vioelt
 * @Title LockLazySingleton
 * @Description:
 * @date 2020/6/28 8:51
 */
public class LockLazySingleton {
    //由于new LockLazySingleton()非原子操作,使用volatile防止重排序
    private volatile static LockLazySingleton instance;

    public LockLazySingleton() {
    }
    
    public  static LockLazySingleton getInstance() {
        if (instance == null) {
            //防止多线程,实现代码块控制,减少资源浪费
            synchronized (LockLazySingleton.class) {
                if (instance == null) {
                    instance = new LockLazySingleton();
                }
            }
        }
        return instance;
    }
}
1.3饿汉模式(静态常量)

类加载较慢,但获取对象的速度快,并且避免了多线程问题,但是该对象一直到程序结束才会去释放。并且将该对象存入全局,是用static修饰

/**
 * TODO(描述)
 *  饿汉模式(静态常量)
 * @author vioelt
 * @Title HuangrySingleton
 * @Description:
 * @date 2020/6/28 8:51
 */
public class HuangrySingleton {
     
     private static HuangrySingleton instance = new HuangrySingleton();  
     private HuangrySingleton(){
     }
     public static HuangrySingleton getInstance() {  
     return instance;  
     }  
 }
1.4饿汉模式(静态代码块)

同上,唯一区别是,上面是用的new的方式直接创建,这里使用static在类加载的时候执行static里面的方法来创建对象,写法不同

/**
 * TODO(描述)
 *  饿汉模式
 * @author vioelt
 * @Title HuangryStaticSingleton
 * @Description:
 * @date 2020/6/28 9:00
 */
public class HuangryStaticSingleton {
    private static HuangryStaticSingleton instance;

    static {
        instance = new HuangryStaticSingleton();
    }

    private HuangryStaticSingleton() {}

    public static HuangryStaticSingleton getInstance() {
        return instance;
    }
}
1.5静态内部类

与饿汉模式加载模式类似,通过类加载方式来保证初始化实例的时候只有一个对象,而使用了静态内部类初始化,可以保证只是在需要的时候才会完成

InnerSingleton的实例的初始化。

与上面四种相比,延迟加载效率比较高,避免了线程不安全

 * TODO(描述)
 *  静态内部类
 * @author vioelt
 * @Title InnerSingleton
 * @Description:
 * @date 2020/6/28 9:00
 */
public class InnerSingleton {
    private InnerSingleton() {}

    private static class SingletonInstance {
        private static final InnerSingleton INSTANCE = new InnerSingleton();
    }

    public static InnerSingleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}

虽然静态内部类碾压了上面的4种方式,适用于大部分场景,但是私有的构造方法只能阻止(并且都需要将构造方法进行私有化防止别人直接通过new的方法创建对象),不能直接通过new的方式创建对象,阻止不了反射的攻击,

在这里插入图片描述

2.反射破解(静态内部类举例)

/**
 * TODO(描述)
 *  反射破解
 * @author zzx
 * @Title Test
 * @Description:
 * @date 2020/6/28 8:51
 */
public class Test {
    public static void main(String[] args) throws Exception {
        InnerSingleton instance = InnerSingleton.getInstance();
        // 拿到所有的构造函数,包括非public的
        Constructor<InnerSingleton> constructor = InnerSingleton.class.getDeclaredConstructor();
        //暴力获取私有方法
        constructor.setAccessible(true);
        //通过newInstance()方法创建,该方法的局限生成对象只能调用无参的构造函数
        InnerSingleton innerSingleton = constructor.newInstance();
        System.out.println(instance);
        System.out.println(innerSingleton);
        System.out.println(instance==innerSingleton);
    }
}

**结果 : **
*在这里插入图片描述

3.序列化操作对象,查看对象是否为同一个对象

/**
 * TODO(描述)
 *  序列化操作对象
 * @author zzx
 * @Title Test
 * @Description:
 * @date 2020/6/28 8:51
 */
public class Test {
    public static void main(String[] args) throws Exception {
        InnerSingleton s = InnerSingleton.getInstance();
   //ObjectOutputStream对象输出流,将InnerSingleton写入test.text文件,注意InnerSingleton需要实现Serializable,否则会报错,从而实现序列化和存储
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(new File("test.text")));
        //将对象进行写入
        oos.writeObject(s);
        oos.close();
        //然后将对象写出
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("test.text"));
        InnerSingleton innerSingleton=(InnerSingleton) ois.readObject();
        ois.close();
        System.out.println(s);
        System.out.println(innerSingleton);
        System.out.println(innerSingleton==s);
    }
}

在这里插入图片描述

4.强大的枚举单例模式

反射安全,序列化/反序列化安全,写法简单

/**
 * TODO(描述)
 *  枚举单例
 * @author zzx
 * @Title SingletonEnum
 * @Description:
 * @date 2020/6/28 10:46
 */
public class SingletonEnum {
     INSTANCE;
    private Object date;

    public Object getDate() {
        return date;
    }

    public void setDate(Object date) {
        this.date = date;
    }
    public static EnumInstance getInstance(){
        return INSTANCE;
    }
}

枚举序列化操作(结果相同)
public class Test {
    public static void main(String[] args) throws Exception {
        SingletonEnum instance = SingletonEnum.getInstance();
   //ObjectOutputStream对象输出流,将InnerSingleton写入test.text文件,注意InnerSingleton需要实现Serializable,否则会报错,从而实现序列化和存储
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(new File("test.text")));
        //将对象进行写入
        oos.writeObject(instance);
        oos.close();
        //然后将对象写出
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("test.text"));
        SingletonEnum innerSingleton=(SingletonEnum) ois.readObject();
        ois.close();
        System.out.println(instance);
        System.out.println(innerSingleton);

    }
}

在这里插入图片描述

反射操作(会抛出异常)
public class Test {
    public static void main(String[] args) throws Exception {
         Class object = SingletonEnum.class;
        Constructor constructor = object.getDeclaredConstructor();
        constructor.setAccessible(true);

        SingletonEnum instance = SingletonEnum.getInstance();
        SingletonEnum newInstance = (SingletonEnum) constructor.newInstance();

        System.out.println(instance.getDate());
        System.out.println(newInstance.getDate());
        System.out.println(instance.getDate() == newInstance.getDate());

    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值