单例模式
-
一、单例模式(Singleton Pattern)
- 介绍:单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并 提供一个全局访问点。单例模式是创建型模式。
单例模式是创建型模式。
隐藏其所有的构造方法 - 使用场景
- 确保任何情况下 都只有一个实例,java中涉及如下:
- ServletContext
- ServletConfig
- ApplicationContext
- DBPool
- 确保任何情况下 都只有一个实例,java中涉及如下:
- 单例的常见写法
- 优点:
- 在内存中只有一个实例,减少了内存开销。
- 可以避免对资源的多重占用。
- 设置全局访问点,严格控制访问。
- 缺点:
- 没有接口,扩展困难。(违背开闭原则)
- 如果要扩展单例对象,只有修改代码,没有其他途径。
- 介绍:单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并 提供一个全局访问点。单例模式是创建型模式。
代码如下
// 懒汉式
//缺点:浪费内存空间
// 1. 常规写法
public class HungrySingleton {
private static final HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
//2.静态单例
public class HungryStaticSingleton {
private static final HungryStaticSingleton hungrySingleton;
static {
hungrySingleton = new HungryStaticSingleton();
}
private HungryStaticSingleton(){}
public static HungryStaticSingleton getInstance(){
return hungrySingleton;
}
}
// 饿汉式
//1.懒汉式单例
//在外部需要使用的时候才进行实例化
public class LazySimpleSingleton {
private LazySimpleSingleton(){}
//静态块,公共内存区域
private static LazySimpleSingleton lazy = null;
//不加synchronized 线程是不安全的,加了synchronized 会变慢,虽然jdk 1.6 之后优化了
// public static LazySimpleSingleton getInstance(){
public synchronized static LazySimpleSingleton getInstance(){
if(lazy == null){
lazy = new LazySimpleSingleton();
}
return lazy;
}
}
//2. 双重锁懒汉式单例
public class LazyDoubleCheckSingleton {
//volatile 防止指令重排序,下面1234 是jvm的指令顺序
private volatile static LazyDoubleCheckSingleton lazy = null;
private LazyDoubleCheckSingleton(){}
public static LazyDoubleCheckSingleton getInstance(){
if(lazy == null){
synchronized (LazyDoubleCheckSingleton.class){
if(lazy == null){
lazy = new LazyDoubleCheckSingleton();
//1.分配内存给这个对象
//2.初始化对象
//3.设置lazy指向刚分配的内存地址
//4.初次访问对象
}
}
}
return lazy;
}
}
//3.内部类懒汉式
//这种形式兼顾饿汉式的内存浪费,也兼顾synchronized性能问题
//完美地屏蔽了这两个缺点
//史上最牛B的单例模式的实现方式
public class LazyInnerClassSingleton {
//默认使用LazyInnerClassGeneral的时候,会先初始化内部类
//如果没使用的话,内部类是不加载的
//TODO1 start
private LazyInnerClassSingleton(){
if(LazyHolder.LAZY != null){
throw new RuntimeException("不允许创建多个实例");
}
}
//TODO1 end
//TODO1 防止被反射攻击,反射出来的单例,就破坏了单例,可以用以下代码测试
/*
//很无聊的情况下,进行破坏
Class<?> clazz = LazyInnerClassSingleton.class;
//通过反射拿到私有的构造方法
Constructor c = clazz.getDeclaredConstructor(null);
//强制访问
c.setAccessible(true);
//暴力初始化
Object o1 = c.newInstance();
//调用了两次构造方法,相当于new了两次
//犯了原则性问题,
Object o2 = c.newInstance();
*/
//每一个关键字都不是多余的
//static 是为了使单例的空间共享
//保证这个方法不会被重写,重载
public static final LazyInnerClassSingleton getInstance(){
//在返回结果以前,一定会先加载内部类
return LazyHolder.LAZY;
}
//默认不加载
private static class LazyHolder{
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
}
// 注册式单例
//枚举式单例
//常量中去使用,常量不就是用来大家都能够共用吗?
//通常在通用API中使用
//ObjectInputStream 中源代码保证了其底层的安全性-序列化破坏单例无效
//同时也防止反射 jdk中源码保证其安全性 源码入口:Constructor.newInstance
//if ((clazz.getModifiers() & Modifier.ENUM) != 0)
// throw new IllegalArgumentException("Cannot reflectively create enum objects");
public enum EnumSingleton {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance(){
return INSTANCE;
}
}
// jad 反编译一下
public final class EnumSingleton extends Enum
{
public static EnumSingleton[] values()
{
return (EnumSingleton[])$VALUES.clone();
}
public static EnumSingleton valueOf(String name)
{
return (EnumSingleton)Enum.valueOf(*/pattern/singleton/register/EnumSingleton, name);
}
private EnumSingleton(String s, int i)
{
super(s, i);
}
public Object getData()
{
return data;
}
public void setData(Object data)
{
this.data = data;
}
public static EnumSingleton getInstance()
{
return INSTANCE;
}
public static final EnumSingleton INSTANCE;
private Object data;
private static final EnumSingleton $VALUES[];
// 饿汉式 线程安全的
static
{
INSTANCE = new EnumSingleton("INSTANCE", 0);
$VALUES = (new EnumSingleton[] {
INSTANCE
});
}
}
//容器式单例
//Spring中的做法,就是用这种注册式单例
public class ContainerSingleton {
private ContainerSingleton(){}
private static Map<String,Object> ioc = new ConcurrentHashMap<String,Object>();
public static Object getInstance(String className){
synchronized (ioc) {
if (!ioc.containsKey(className)) {
Object obj = null;
try {
obj = Class.forName(className).newInstance();
ioc.put(className, obj);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
} else {
return ioc.get(className);
}
}
}
}
//ThreadLocal线程单例
//TODO ThreadLocal 不能保证其 创建的对象是全局唯一,但是能保证在单个线程中是唯一的,天生的线程安全
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
new ThreadLocal<ThreadLocalSingleton>(){
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
private ThreadLocalSingleton(){}
public static ThreadLocalSingleton getInstance(){
return threadLocalInstance.get();
}
}
// 划重点 单例之敌 序列化 反序列化
//反序列化时导致单例破坏
public class SeriableSingleton implements Serializable {
//序列化就是说把内存中的状态通过转换成字节码的形式
//从而转换一个IO流,写入到其他地方(可以是磁盘、网络IO)
//内存中状态给永久保存下来了
//反序列化
//讲已经持久化的字节码内容,转换为IO流
//通过IO流的读取,进而将读取的内容转换为Java对象
//在转换过程中会重新创建对象new
public final static SeriableSingleton INSTANCE = new SeriableSingleton();
private SeriableSingleton(){}
public static SeriableSingleton getInstance(){
return INSTANCE;
}
// TODO 该方法是重点,可以看下源码
// 源码入口 ObjectInputStream.readObject
/*
1.ObjectInputStream.readObject
2.public final Object readObject()
3.case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));
4. desc.hasReadResolveMethod()
5.readResolveMethod = getInheritableMethod( cl, "readResolve", null, Object.class);
6.Object rep = desc.invokeReadResolve(obj);
*/
//
private Object readResolve(){
return INSTANCE;
}
}
// TEST demo
public static void main(String[] args) {
SeriableSingleton s1 = null;
SeriableSingleton s2 = SeriableSingleton.getInstance();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("SeriableSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("SeriableSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (SeriableSingleton)ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);
} catch (Exception e) {
e.printStackTrace();
}
}