单例模式
单例模式确保程序运行过程中一个特定的类只有一个实例对象,并提供对这个对象的外部访问
常见的单例模式应用如线程池、缓存、日志对象等
特点:
单例类必须自己创建自己的唯一实例,并且给其它对象提供这一个实例
应用场景:
1.工具类
2.需要重复创建/销毁的资源消耗过大的类
3.等等
饿汉式单例:程序一启动就创建对象实例,占用内存资源,提高程序效率(典型的以空间换时间)
懒汉式单例:程序一启动不创建对象实例,什么时候用到对象实例什么时候创建,节省资源
饿汉式单例
通过静态变量实现的单例
私有化静态变量和构造器不允许外界访问
提供供外界访问变量的方法
public class HungrySingleton {
private static final HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
测试
@Test
public void test01 (){
HungrySingleton instance = HungrySingleton.getInstance();
HungrySingleton instance2 = HungrySingleton.getInstance();
System.out.println(instance); // com.design.demo.singleton.HungrySingleton@1d6d1d42
System.out.println(instance2); // com.design.demo.singleton.HungrySingleton@1d6d1d42
}
静态代码块的实现
public class HungrySingleton {
private static final HungrySingleton hungrySingleton ;
static {
hungrySingleton= new HungrySingleton();
}
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
懒汉式单例
在调用的时候创建对象
public class LazyInnerClassSingleton {
private static LazyInnerClassSingleton lazySingleton = null;
public static LazyInnerClassSingleton getInstance(){
if(lazySingleton == null){
lazySingleton = new LazyInnerClassSingleton();
}
return lazySingleton;
}
}
这种方式的懒汉式单例在多线程环境会有安全隐患,可以使用synchronized关键字来增强
synchronized在线程较多时会导致程序性能大幅下降,此时可以使用双重检查锁增强
public class LazyInnerClassSingleton {
private static volatile LazyInnerClassSingleton lazySingleton = null;
// volatile关键字禁止指令重排
public static LazyInnerClassSingleton getInstance(){
if(lazySingleton == null){
synchronized (LazyInnerClassSingleton.class){
if(lazySingleton == null){
lazySingleton = new LazyInnerClassSingleton();
//(1)在堆上开辟空间;(2)属性初始化;(3)引用指向对象
}
}
}
return lazySingleton;
}
}
synchronized 对性能还是会有影响,这时可以使用静态内部类在类初始化的时候进行优化
public class LazyInnerClassSingleton {
// 默认先初始化内部类
public static LazyInnerClassSingleton getInstance(){
// 返回之前,一定会加载内部类
return LazyInner.lazy;
}
private static class LazyInner{
private static final LazyInnerClassSingleton lazy = new LazyInnerClassSingleton();
}
}
反射破坏单例
通过反射创建上述对象,可以看出反射可以破坏单例
@Test
public void test2(){ // 反射破坏对象
try{
// 1.获取class对象
Class<LazyInnerClassSingleton> lazyInnerClassSingletonClass = LazyInnerClassSingleton.class;
// 2.获取构造器对象(包括private)
Constructor<LazyInnerClassSingleton> declaredConstructor = lazyInnerClassSingletonClass.getDeclaredConstructor(null);
// 3.允许强制访问
declaredConstructor.setAccessible(true);
// 4.暴力初始化
LazyInnerClassSingleton l1 = declaredConstructor.newInstance();
LazyInnerClassSingleton l2 = declaredConstructor.newInstance();
System.out.println(l1);// com.design.demo.bean.LazyInnerClassSingleton@123772c4
System.out.println(l2);// com.design.demo.bean.LazyInnerClassSingleton@2d363fb3
System.out.println(l2 == l1);// false
}catch (Exception e){
System.out.println("发生了异常");
}
}
此时可以在构造器中对反射调用的构造器进行条件限制
public class LazyInnerClassSingleton {
// 在构造器中对内部类的静态属性进行校验即可,反射会调用这个构造器方法
private LazyInnerClassSingleton(){
if(LazyInner.lazy != null){
throw new RuntimeException("不允许创建多个实例");
}
}
// 默认先初始化内部类
public static LazyInnerClassSingleton getInstance(){
// 返回之前,一定会加载内部类
return LazyInner.lazy;
}
private static class LazyInner{
private static final LazyInnerClassSingleton lazy = new LazyInnerClassSingleton();
}
}
序列化破坏单例
序列化:将对象持久化保存到硬盘中
反序列化:把硬盘中的对象读取到内存中
这个过程中的对象也不是单例的
对象序列化需要实现Serializable接口
public class LazyInnerClassSingleton implements Serializable{
private LazyInnerClassSingleton(){
if(LazyInner.lazy != null){
throw new RuntimeException("不允许创建多个实例");
}
}
// 默认先初始化内部类
public static LazyInnerClassSingleton getInstance(){
// 返回之前,一定会加载内部类
return LazyInner.lazy;
}
private static class LazyInner{
private static final LazyInnerClassSingleton lazy = new LazyInnerClassSingleton();
}
}
序列化对象
@Test
public void test4(){ // 序列化破坏单例
LazyInnerClassSingleton l1 = null;
LazyInnerClassSingleton l2 = LazyInnerClassSingleton.getInstance();
FileOutputStream fos = null;
try{
// 1.创建SerializableSingleton.text文件,写入单例对象的数据
fos = new FileOutputStream("SerializableSingleton.text");
// 2.创建对象输出流,需要一个输出流对象,
ObjectOutputStream oos = new ObjectOutputStream(fos);
// 3.将对象输出到文件中
oos.writeObject(l2);
oos.flush();
oos.close();
// 4.创建输入流和对象输入流,将SerializableSingleton.text文件中的对象读取到程序中
FileInputStream fis = new FileInputStream("SerializableSingleton.text");
ObjectInputStream ois = new ObjectInputStream(fis);
l1 =(LazyInnerClassSingleton) ois.readObject();
ois.close();
System.out.println(l1);// com.design.demo.bean.LazyInnerClassSingleton@27082746
System.out.println(l2);// com.design.demo.bean.LazyInnerClassSingleton@368239c8
System.out.println(l2 == l1);// false
}catch (Exception e){
e.printStackTrace();
}
}
根据ObjectInputStream的readOrdinaryObject()源码可知
如果存在readResolve()就调用
所以在实现了Serializable的类中添加readResolve方法就可以防止序列化破环单例
public class LazyInnerClassSingleton implements Serializable {
private LazyInnerClassSingleton(){
if(LazyHolder.LAZY != null){
throw new RuntimeException("不允许创建多个实例");
}
}
private Object readResolve(){
return LazyHolder.LAZY;
}
public static final LazyInnerClassSingleton getInstance(){
return LazyHolder.LAZY;
}
private static class LazyHolder{
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
}