设计模式系列(一)单例模式
1.单例模式
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类 ,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式, 可以直接访问,不需要实例化该类的对象。
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
优点
- 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
- 避免对资源的多重占用(比如写文件操作)。
缺点
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
使用场景:
- 要求生产唯一序列号。
- WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
- 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class)
防止多线程同时进入造成 instance 被多次实例化。
- 饿汉式 静态常量
//饿汉式(静态常亮)在类装载的时候完成了实例化,避免了线程同步问题。但是没有达到懒加载,有可能造成内存的浪费。
class Singleton {
//构造私有化
private Singleton() {
}
//内部创建对象实例
private final static Singleton instance = new Singleton();
//获取实例
public static Singleton getInstance() {
return instance;
}
}
- 饿汉式 静态代码块
//优缺点同上
class Singleton {
//构造私有化
private Singleton() {
}
//内部创建对象实例
private static Singleton instance;
static {
instance = new Singleton();
}
//获取实例
public static Singleton getInstance() {
return instance;
}
}
- 懒汉式 线程不安全
class Singleton {
//构造私有化
private Singleton() {
}
//内部创建对象实例
private static Singleton instance;
//获取实例
public static Singleton getInstance() {
//在使用的时候才去创建(此处会出现线程不安全问题)
if (instance == null)
instance = new Singleton();
return instance;
}
}
- 懒汉式 线程安全 同步方法
class Singleton {
//构造私有化
private Singleton() {
}
//内部创建对象实例
private static Singleton instance;
//获取实例 synchronized让线程依次获取instance
public static synchronized Singleton getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}
}
- 懒汉式 线程安全 同步方法
//虽然可以解决线程不安全问题,但是每次获取instance都走同步方法,会导致效率低
class Singleton {
//构造私有化
private Singleton() {
}
//内部创建对象实例
private static Singleton instance;
//获取实例 synchronized让线程依次获取instance
public static synchronized Singleton getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}
}
- 懒汉式 线程安全 同步代码块(双重判断) volatile修饰变量
//volatile 共享变量 可以在修改后立刻刷新
class Singleton {
//构造私有化
private Singleton() {
}
//内部创建对象实例
private static volatile Singleton instance;
//获取实例 synchronized让线程依次获取instance
public static synchronized Singleton getInstance() {
if (instance == null)
//没有作用 因为已经进入if语句了
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
}
- 静态内部类 (外部类被装载的时候,内部类不会被装载)
class Singleton {
//构造私有化
private Singleton() {
}
//静态内部类,该类中有一个静态的属性,静态内部类只有被调用的时候,才回装载(此处相当于懒加载,可以节省内存) 在类装载的时候,是线程安全的。 而且类只装载一次
private static class SingletonInstance {
private static final Singleton instance = new Singleton();
}
//获取实例 synchronized让线程依次获取instance
public static synchronized Singleton getInstance() {
return SingletonInstance.instance;
}
}
-枚举方式 避免多线程同步问题,防止反序列化重新创建对象
enum Singleton {
INSTANCE;
public void method() {
System.out.println("单例调用");
}
}
单例模式在jdk中的应用
Runtime对象,用到了饿汉式的单例
如何破坏单例,如何防止
-
通过反射破坏单例
虽然构造方法以及被私有化,但还是可以通过反射产生新的对象。//通过反射获取 Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor(); constructor.setAccessible(true); Singleton reflex = constructor.newInstance(); System.out.println("reflex的hashCode:"+reflex.hashCode());
如何防止反射破坏
创建一个全局变量设为true,在构造方法中判断,如果是一个次创建,就把变量设为false,如果不是,就抛出异常。class Singleton { private static boolean isFristCreate = true;//默认是第一次创建 //构造私有化 private Singleton() { if (isFristCreate) { synchronized (Singleton.class) { if (isFristCreate) { isFristCreate = false; } } } else { throw new RuntimeException("已然被实例化一次,不能在实例化"); } } //静态内部类,该类中有一个静态的属性,静态内部类只有被调用的时候,才回装载(此处相当于懒加载,可以节省内存) 在类装载的时候,是线程安全的。 而且类只装载一次 private static class SingletonInstance { private static final Singleton instance = new Singleton(); } //获取实例 synchronized让线程依次获取instance public static synchronized Singleton getInstance() { return SingletonInstance.instance; } }
-
通过克隆方式破坏
-
通过反序列化方式破坏