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());
}
}