单例模式
一.核心作用
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
整个程序只需要一个实例的场景。比如任务管理器,回收站,连接池,线程池。
二.优点
--由于单例模式只生成一个实例,减少系统性能的开销.当一个对象产生需要较多的资源时,可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存来解决.
--单例模式可以在系统设置全局的访问点,优化资源访问.
三.常见实现方式
主要:
饿汉式 (线程安全 调用效率高 不能延时加载)
懒汉式 (线程安全 调用效率不高 能延时加载)
其他:
双重检测锁式 将懒汉式的同步方法改为同步块
静态内部类式 (线程安全 调用效率高 能延时加载)
枚举单例 (线程安全 调用效率高 不能延时加载 可以防止反射和反序列化漏洞)
四.如何选用
占用资源少,不需要延时加载
枚举式 好于 饿汉式
占用资源多,需要延时加载时
静态内部类 好于 懒汉式
五.防止反射漏洞
在构造方法中加一个判断,若对象不等于null,则抛出异常
六.防止反序列化漏洞
在该类中添加readResolve(){ return Instance; }
package com.skd.designMode.simple;
/*
* 全局获取的实例是唯一的
* 1.构造方法私有化
* 2.对外提供静态方法(getInstance)来创建对象
*/
public class Person
{
private static Person person;
// 私有化构造方法
private Person()
{
// 防止反射调用
if (null != person)
{
throw new Exception();
}
}
// 懒汉式
// 延迟加载,方法需要同步,调用效率低
public static synchronized Person getInstance()
{
if (person == null)
{
person = new Person();
}
return person;
}
// 双重检测锁式
// 此处将 person 声明为 volatile 即可得到唯一正确的 person 对象
public static Person getInstance2()
{
// 而且此处可能获得一个创建不完整的对象并返回
// 初始化一个对象的时候,会经历内存分配、初始化、返回对象在堆上的引用等一系列操作,这种方式产生的对象是一个完整的对象,可以正常使用。虚拟机可能会进行指令重排序
if (null == person)
{
synchronized (Person.class)
{
// 此处不判断可能创建多个对象,因为第一次判空未加锁
if (null == person)
{
person = new Person();
}
}
}
return person;
}
// 防止反序列化
public static Person readResolve()
{
return person;
}
}
// 饿汉式
class People
{
// 类初始化时立即加载该对象,虚拟机的同步机制保证其线程安全!
// 无法延时加载
private static People people = new People();
// 私有化构造方法
private People()
{
}
// 方法不需要同步,调用效率高
public static People getInstance()
{
return people;
}
}
// 静态内部类式
// 这种方式线程安全,调用效率高,延时加载
class Teacher
{
// 私有化构造方法
private Teacher(){}
// 静态内部类
private static class Inner
{
// 类初始化时立即加载该对象,虚拟机的同步机制保证其线程安全!
static final Teacher teacher = new Teacher();
}
// 返回静态内部类的静态属性teacher
public static Teacher getInstance()
{
return Inner.teacher;
}
}
// 枚举式
package com.skd.simple;
public enum Simple
{
// 枚举本身也是一种单例
// 线程安全,效率高,无法延时加载
INSTANCE(new Object());
Object obj;
HAHA(Object object)
{
obj = object;
}
}
如果文章对您有所帮助,可以点一下推荐