写在前面的话
复习、总结23种设计模式
上一篇
单例模式
定义
单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局的访问点。
单例模式的适用场景
确保任何情况下都绝对只有一个实例。
比如:ServletContext、ServletConfig、ApplicationContext、DBPool等
1.饿汉式单例
定义:在单例类被首次加载时,就创建实例
优点:执行效率高,性能高,没有任何的锁
缺点:某些情况下,可能会造成内存浪费
// 饿汉式:典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用,
// 先创建出来,然后每次调用的时候,就不需要判断了,节省了运行时间
public class HungrySingleton {
private static final HungrySingleton hungrysingleton = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return hungrysingleton;
}
}
package com.design.pattern.test01;
/*
static类加载的时候就创建,final:不可被覆盖。
内存浪费,占着茅坑不拉屎
*/
public class Test01饿汉式单例 {
public static void main(String[] args) {
HungrySingleton instance1 = HungrySingleton.getInstance();
HungrySingleton instance2 = HungrySingleton.getInstance();
System.out.println(instance1 == instance2); // true
}
}
2.懒汉式单例
定义:被外部类调用时才创建实例
优点:执行效率高,性能高,没有任何的锁;节约内存
缺点:线程不安全,多线程情况下,就有可能创建多个实例,不能保证一个实例
package com.design.pattern.test02;
public class LazySingleton {
//2.本类内部创建对象实例
private static LazySingleton singleton;
/**
* 1.构造方法私有化,外部不能new
*/
private LazySingleton() {
}
//3.提供一个公有的静态方法,返回实例对象
public static LazySingleton getInstance() {
if (singleton == null) {
singleton = new LazySingleton();
}
return singleton;
}
}
package com.design.pattern.test02;
/*
线程不安全
多线程情况下,就有可能创建多个实例,不能保证一个实例
*/
public class Test02懒汉式单例 {
public static void main(String[] args) {
LazySingleton instance1 = LazySingleton.getInstance();
LazySingleton instance2 = LazySingleton.getInstance();
System.out.println(instance1 == instance2);
}
}
3.懒汉式单例(增加synchronized保证线程安全)
定义:被外部类调用时才创建实例,利用synchronized保证线程安全
优点:线程安全;节约内存
缺点:synchronized关键字导致性能低下,在高并发场景下
package com.design.pattern.test03;
public class LazySingleton {
private static LazySingleton singleton;
private LazySingleton() {
}
public static synchronized LazySingleton getInstance() {
if (singleton == null) {
singleton = new LazySingleton();
}
return singleton;
}
}
package com.design.pattern.test03;
/*
在test02的基础上加上synchronized关键字
缺点:synchronized关键字导致性能低下,在高并发场景下
*/
public class Test03懒汉式单例 {
public static void main(String[] args) {
LazySingleton instance1 = LazySingleton.getInstance();
LazySingleton instance2 = LazySingleton.getInstance();
System.out.println(instance1 == instance2);
}
}
4.懒汉式单例(双重校验锁DCL)
定义:被外部类调用时才创建实例,利用DCL保证线程安全
优点:线程安全;节约内存
缺点:可读性不高,代码不够优雅
package com.design.pattern.test04;
public class LazySingleton {
//volatile:内存可见性+防止指令重排
private static volatile LazySingleton singleton;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (singleton == null) {
synchronized (LazySingleton.class) {
if (singleton == null) {
LazySingleton temp = new LazySingleton();
singleton = temp;
}
}
}
return singleton;
}
}
package com.design.pattern.test04;
/*
双重校验锁DCL
性能提高了,线程安全了。
但是可读性不高,代码不够优雅,
*/
public class Test04懒汉式单例DCL {
public static void main(String[] args) {
LazySingleton instance1 = LazySingleton.getInstance();
LazySingleton instance2 = LazySingleton.getInstance();
System.out.println(instance1 == instance2);
}
}
5.静态内部类单例
定义:利用静态内部类保证单例
优点:写法优雅,利用了java本身的语法特点,性能高,避免了内存浪费
缺点:能够被反射破坏
package com.design.pattern.test05;
/**
* 当getInstance方法第一次被调用的时候,它第一次读取LazyHolder.instance,导致LazyHolder类得到初始化;
* 而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,
* 因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性
*/
public class LazyStaticInnerClassSingleton {
private LazyStaticInnerClassSingleton() {
}
public static LazyStaticInnerClassSingleton getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder {
private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
}
}
package com.design.pattern.test05;
/*
静态内部类单例写法
优点:写法优雅,利用了java本身的语法特点,性能高,避免了内存浪费
缺点:能够被反射破坏
*/
public class Test05静态内部类单例 {
public static void main(String[] args) {
LazyStaticInnerClassSingleton instance1 = LazyStaticInnerClassSingleton.getInstance();
LazyStaticInnerClassSingleton instance2 = LazyStaticInnerClassSingleton.getInstance();
System.out.println(instance1 == instance2);
}
}
6.静态内部类单例,防止被反射破坏单例
定义:利用静态内部类保证单例,可以防止被反射破坏
优点:写法优雅,利用了java本身的语法特点,性能高,避免了内存浪费
缺点:可以防止被反射破坏
package com.design.pattern.test06;
public class LazyStaticInnerClassSingleton {
private LazyStaticInnerClassSingleton() {
if (LazyHolder.INSTANCE != null) {
throw new RuntimeException("防止反射破坏单例");
}
}
public static LazyStaticInnerClassSingleton getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder {
private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
}
}
package com.design.pattern.test06;
/*
静态内部类单例写法
优点:写法优雅,利用了java本身的语法特点,性能高,避免了内存浪费
防止被反射破坏
*/
public class Test06静态内部类单例防止反射破坏 {
public static void main(String[] args) {
LazyStaticInnerClassSingleton instance1 = LazyStaticInnerClassSingleton.getInstance();
LazyStaticInnerClassSingleton instance2 = LazyStaticInnerClassSingleton.getInstance();
System.out.println(instance1 == instance2);
}
}
package com.design.pattern.test06;
import java.lang.reflect.Constructor;
// 尝试反射破坏单例
public class ReflectTest {
public static void main(String[] args) {
try {
Class<?> clazz = LazyStaticInnerClassSingleton.class;
Constructor c = clazz.getDeclaredConstructor(null);
c.setAccessible(true);
Object instance = c.newInstance();
System.out.println(instance);
} catch (Exception e) {
e.printStackTrace();
}
}
}
7.注册式单例
定义:将每一个实例都缓存到统一的容器中,使用唯一标识获取实例
优点:
缺点:
package com.design.pattern.test07;
public enum EnumSingleton {
INSTANCE;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
private Object data;
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
package com.design.pattern.test07;
import java.lang.reflect.Constructor;
/*
注册式单例
将每一个实例都缓存到统一的容器中,使用唯一标识获取实例
*/
public class Test07注册式单例 {
public static void main(String[] args) {
EnumSingleton instance = EnumSingleton.getInstance();
instance.setData(new Object());
try {
Class clazz = EnumSingleton.class;
Constructor c = clazz.getDeclaredConstructor();
Object o = c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
}
8.容器氏单例
定义:利用Map的key不重复存储单例对象
优点:
缺点:
package com.design.pattern.test08;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ContainerSingleton {
private ContainerSingleton() {
}
private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();
public static Object getInstance(String className) {
Object instance = null;
if (!ioc.containsKey(className)) {
try {
instance = Class.forName(className).newInstance();
ioc.put(className, instance);
} catch (Exception e) {
e.printStackTrace();
}
return instance;
} else {
return ioc.get(className);
}
}
}
package com.design.pattern.test08;
/*
容器氏单例,线程安全解决办法:加锁,锁住(IOC)容器
* */
public class Test08容器式单例 {
public static void main(String[] args) {
Object instance1 = ContainerSingleton.getInstance("com.design.pattern.test08.ContainerSingleton");
Object instance2 = ContainerSingleton.getInstance("com.design.pattern.test08.ContainerSingleton");
System.out.println(instance1 == instance2);
}
}
9.持久化序列化单例
定义:将对象序列化存储到文件中,再读取这个文件反序列化生成该对象
优点:
缺点:
package com.design.pattern.test09;
import java.io.Serializable;
public class SerializableSingleton implements Serializable {
//序列化
//把内存中的对象的状态转化为字节码的形式
//把字节码通过IO输出流,写到磁盘!
//永久的保存下来
public final static SerializableSingleton INSTANCE = new SerializableSingleton();
private SerializableSingleton() {
}
public static SerializableSingleton getInstance() {
return INSTANCE;
}
// 反序列化的核心
private Object readResolve() {
return INSTANCE;
}
}
package com.design.pattern.test09;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/*
* */
public class Test09持久化序列化单例 {
public static void main(String[] args) {
try {
SerializableSingleton s2 = SerializableSingleton.getInstance();
{
FileOutputStream fos = new FileOutputStream("filename");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
}
SerializableSingleton s1 = null;
{
FileInputStream fis = new FileInputStream("filename");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (SerializableSingleton) ois.readObject();
ois.close();
}
System.out.println(s1 == s2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
10.ThreadLocal单例
定义:保证一个线程内是单例的
优点:
缺点:
package com.design.pattern.test10;
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 Test10ThreadLocal单例 {
public static void main(String[] args) {
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
System.out.println(ThreadLocalSingleton.getInstance());
}
}
总结
学习单例模式的知识重点总结
- 私有化构造器
- 保证线程安全
- 延迟加载
- 防止序列化和反序列化破坏单例
- 防御反射攻击单例