Java 学习笔记——单例设计模式
概述
概念
单例(Singleton)设计模式是结构最简单的设计模式。其内部只包含一个称为单例类的特殊类。
单例模式的目的是为了确保系统中的一个类只有一个实例,并且这个实例可以很方便的被外界访问。
单例模式的结构
在单例类的内部创建唯一实例,通过静态方法getInstance()使得客户端可以使用它的唯一的实例。同时将构造器私有化,将唯一实例定义为静态对象供外部共享访问
单例模式的适用环境
1)系统只需要一个实例对象,或因为其资源消耗量大,只允许创建一个对象
2)客户调用类的单个实例只允许使用一个公共访问点。
单例模式的实现
单例模式思路分析
1)构造器私有化
2)提供一个该类的静态私有化实例
3)提供一个公有的静态方法,提供该实例
实现方式I:饿汉式
class Singleton{
//实例化私有的静态成员变量
private static final Singleton instance = new Singleton();
//私有化构造器
private Singleton(){}
//公有的静态工厂方法
public static Singleton getInstance(){
return instance;
}
}
实现方式II:懒汉式
class Singleton1{
//私有成员变量
private volatile static Singleton1 instance = null;
//私有化构造器
private Singleton1(){};
//公有的静态方法
public static Singleton1 getInstance(){
//判断instance是否为空,若不为空则直接返回
if(instance == null){
//加锁,实例化instance
synchronized (Singleton1.class){
//二次判断(避免多个线程在加锁前均通过第一次判断,因而创建两个实例)
if(instance == null){
//实例化instance
instance = new Singleton1();
}
}
}
return instance;
}
}
两种创建方式对比分析
1)饿汉式:在类加载时就将自己实例化,不存在线程安全的问题,可以确保实例的唯一性;同时调用速度与反应时间也更快。但该实例生命周期长,资源利用效率差。
2)懒汉式:在getInstance方法被调用时才实例化对象,系统资源占用率低。但需要额外处理线程安全问题,效率相对较低。
破坏单例模式的方法
反射
通过反射我们可以获取单例类的构造函数,通过设置setAccessible(true)的方法,可以使我们访问私有的构造方法,这样以来单例模式就有可能被破坏
public class SingletonTesting {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<SingleTon> clazz = SingleTon.class;
//通过反射获取单例类的构造器
Constructor<SingleTon> constructor = clazz.getDeclaredConstructor();
//禁用安全检查
constructor.setAccessible(true);
//创建两个对象
SingleTon ton1 = constructor.newInstance();
SingleTon ton2 = constructor.newInstance();
System.out.println(ton1 == ton2); //控制台输出false,单例模式被破坏
}
}
序列化:
在序列化,反序列化的过程中,底层也可以通过反射的方式破坏单例模式
public class SingletonTesting {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("test"));
SingleTon instance1 = SingleTon.getInstance();
//对获取的单例对象进行序列化
out.writeObject(instance1);
ObjectInputStream in = new ObjectInputStream(new FileInputStream("test"));
//对获取的单例对象进行反序列化
SingleTon instance2 = (SingleTon)in.readObject();
System.out.println(instance1 == instance2); //控制台输出false,单例模式被破坏
}
}
防止单例模式被破坏的方法
运用枚举类实现单例模式
enum Singleton3{
INSTANCE;
public static Singleton3 getInstance(){
return INSTANCE;
}
}
测试
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("test"));
Singleton3 instance1 = Singleton3.getInstance();
//对获取的单例对象进行序列化
out.writeObject(instance1);
ObjectInputStream in = new ObjectInputStream(new FileInputStream("test"));
//对获取的单例对象进行反序列化
Singleton3 instance2 = (Singleton3) in.readObject();
System.out.println(instance1 == instance2); //控制台输出true
}