源代码
GitHub源代码
1.本文目标
本文目标是为了让大家认识并理单例设计模式。
2.基本套路
定义:保证一个类仅有一个实例,并提供一个全局访问点
类型:创建型
选择关键点:一个对象在应用中出现多个实例是否会引起逻辑上或者是程序上的错误
设计原则:无
使用概率:99.99999%
难度系数:低
3.适用场景
1.想确保任何情况下都绝对只有一个实例
4.使用步骤
单例模式有多种方式实现,我们一一举例子
5.举个栗子
我们用具体的代码去更好的理解这个设计模式
5.1栗子说明
- 目的:希望全局只有一个对象
5.2饿汉式
实现代码如下:
步骤1.构造函数私有化
步骤2.创建全局静态final变量
步骤3.对外提供一个静态方法
public class HungrySingleton implements Serializable{
//步骤1:构造函数私有化
private HungrySingleton() {
//防止反射破解,这一招对饿汉式和静态内部类都好使,对懒汉式不好使
if(HUNGRYSINGLETON!=null){
throw new RuntimeException("单例构造器禁止反射调用");
}
}
//步骤2:创建全局静态final变量
private static final HungrySingleton HUNGRYSINGLETON = new HungrySingleton();
//步骤3:对外提供一个静态方法
public static HungrySingleton getInstance() {
return HUNGRYSINGLETON;
}
/**
* 一旦单例模式涉及到序列化和反序列化,一定要小心单例被破坏掉
* 这个方法是反射调用的,如果不写这个方法,序列化就会破坏单例(翻看ObjectInputStream源码得知的)
*/
private Object readResolve(){
return HUNGRYSINGLETON;
}
}
测试类:
public static void main(String[] args) {
//正常方式创建对象
HungrySingleton oldInstance = HungrySingleton.getInstance();
//反射的方式创建
Class objectClass= HungrySingleton.class;
Constructor constructor =objectClass.getDeclaredConstructor();
constructor.setAccessible(true);//暴力访问
HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
//比较其地址值
System.out.println("oldInstance: "+oldInstance);
System.out.println("newInstance"+newInstance);
System.out.println(oldInstance==newInstance);
}
5.3静态内部类方式
实现代码如下:
步骤1.构造函数私有化
步骤2.创建静态内部类对象
步骤3.对外提供一个静态方法
public class StaticInnerClassSingleton {
//步骤1:构造函数私有化
private StaticInnerClassSingleton(){
//防止反射破解,这一招对饿汉式和静态内部类单例都好使,对懒汉式不好使
if(Holder.INSTANCE!=null){
throw new RuntimeException("单例构造器禁止反射调用");
}
}
//步骤3:对外提供一个静态方法
public static StaticInnerClassSingleton getInstance() {
return Holder.INSTANCE;
}
/**
* 步骤2:创建静态内部类对象
* 静态内部类
*/
private static class Holder {
private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
}
}
测试类:
public static void main(String[] args) {
StaticInnerClassSingleton single1 = StaticInnerClassSingleton.getInstance();
StaticInnerClassSingleton single2 = StaticInnerClassSingleton.getInstance();
//比较其地址值
System.out.println("single1: "+single1);
System.out.println("single2"+single2);
System.out.println(single1==single2);
}
5.4双重校验懒汉式单例
实现代码如下:
步骤1.构造函数私有化
步骤2.创建全局静态私有变量
步骤3.对外提供一个静态方法,里面进行了双重校验
public class LazyDoubleCheckSingleton {
//步骤1:构造函数私有化
private LazyDoubleCheckSingleton() {
/**
* 防止反射破解,这一招对饿汉式和静态内部类都好使,对懒汉式不好使
* 因为,如果反射的创建方式先进来,就能直接创建对象.
* 然后正常的创建方式后进来,又能创建对象,就把这个单例破坏了.
*/
if(mInstance!=null){
throw new RuntimeException("单例构造器禁止反射调用");
}
}
//步骤2:创建全局静态私有变量
private static LazyDoubleCheckSingleton mInstance = null;
//步骤3:对外提供一个静态方法,里面进行了双重校验
public static LazyDoubleCheckSingleton getInstance() {
if (mInstance == null) {
synchronized (LazyDoubleCheckSingleton.class) {
if (mInstance == null) {
mInstance = new LazyDoubleCheckSingleton();
}
}
}
return mInstance;
}
}
测试类:
public static void main(String[] args) {
//反射的方式创建
Class objectClass= LazyDoubleCheckSingleton.class;
Constructor constructor =objectClass.getDeclaredConstructor();
constructor.setAccessible(true);//暴力访问
LazyDoubleCheckSingleton newInstance = (LazyDoubleCheckSingleton) constructor.newInstance();
//正常方式创建
LazyDoubleCheckSingleton oldInstance = LazyDoubleCheckSingleton.getInstance();
//比较其地址值
System.out.println("oldInstance: "+oldInstance);
System.out.println("newInstance"+newInstance);
}
5.5枚举式单例
实现代码如下:
public enum EnumInstance {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumInstance getInstance(){
return INSTANCE;
}
}
测试类:
public static void main(String[] args) {
//正常方式创建
EnumInstance oldInstance = EnumInstance.getInstance();
Object newData1 = oldInstance.getData();
Object newData2 = oldInstance.getData();
//比较其地址值
System.out.println("newData1: "+newData1);
System.out.println("newData2"+newData2);
System.out.println(newData1==newData2);
}
5.6容器的单例模式
实现代码如下:
public class ContainerSingleton {
private ContainerSingleton() {
}
private static Map<String, Object> singletonMap = new HashMap<>();
public static void putInstance(String key, Object instance) {
if (!singletonMap.containsKey(key)) {
singletonMap.put(key, instance);
}
}
public static Object getInstance(String key) {
return singletonMap.get(key);
}
}
测试类:
public static void main(String[] args) {
ContainerSingleton.putInstance("city","北京");
ContainerSingleton.putInstance("风清扬","独孤九剑");
String str = (String) ContainerSingleton.getInstance("风清扬");
System.out.println(str);
}
6.优点
- 在内存里只有一个实例,减少了内存开销
- 可以避免对资源的多重占用
- 设置了全局访问点,严格控制访问
7.缺点
- 没有接口,扩展困难
8.总结
本文只是对单例模式进行一个分享,接下来会从创建型模式,结构型模式,行为型模式,这三大类展开一个系列分享,大家可以持续进行关注,信仰年輕的设计模式,蟹蟹啦。
网友评论