文章目录
一.文章推荐
二.创建型模式
创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。
创建型模式在创建什么(What),由谁创建(Who),何时创建(When)等方面都为软件设计者提供了尽可能大的灵活性。创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。
2.1 单例模式
2.1.1单例模式定义
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
2.1.2 为什么使用单例模式
- 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决.
- 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理.
2.1.3 为什么不使用全局变量保证一个类只有一个实例
我们知道全局变量分为静态变量和实例变量,静态变量也可以保证该类的实例只存在一个。
只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。
但是,如果说这个对象非常消耗资源,而且程序某次的执行中一直没用,这样就造成了资源的浪费。利用单例模式的话,我们就可以实现在需要使用时才创建对象,这样就避免了不必要的资源浪费。 不仅仅是因为这个原因,在程序中我们要尽量避免全局变量的使用,大量使用全局变量给程序的调试、维护等带来困难。
三.单例模式的实现
通常单例模式在Java语言中,有两种构建方式:
**饿汉方式:**指全局的单例实例在类装载时构建
**懒汉方式:**指全局的单例实例在第一次被使用时构建。
不管是那种创建方式,它们通常都存在下面几点相似处:
- 单例类必须要有一个 private 访问级别的构造函数,只有这样,才能确保单例不会在系统中的其他代码内被实例化;
- instance 成员变量和 uniqueInstance 方法必须是 static 的。
3.1 饿汉式实现(单例对象立即加载)
public class SingletonDemo02 {
private static /*final*/ SingletonDemo02 s = new SingletonDemo02();
private SingletonDemo02(){} //私有化构造器
public static /*synchronized*/ SingletonDemo02 getInstance(){
return s;
}
}
饿汉式单例模式代码中,static变量会在类装载时初始化,此时也不会涉及多个线程对象访问该对象的问题。虚拟机保证只会装载一次该类,肯定不会发生并发访问的问题。因此,可以省synchronized关键字。
问题:如果只是加载本类,而不是要调用getInstance(),甚至永远没有调用,则会造成资源浪费!
3.2 懒汉式实现(单例对象延迟加载)
public class SingletonDemo01 {
private static SingletonDemo01 s;
private SingletonDemo01(){} //私有化构造器
public static synchronized SingletonDemo01 getInstance(){
if(s==null){
s = new SingletonDemo01();
}
return s;
}
}
要点:lazy load! 延迟加载, 懒加载! 真正用的时候才加载!
问题:资源利用率高了。但是,每次调用getInstance()方法都要同步,并发效率较低。
3.3 懒汉式(双重检查加锁版本)
利用双重检查加锁(double-checked locking),首先检查是否实例已经创建,如果尚未创建,“才”进行同步。这样以来,只有一次同步,这正是我们想要的效果。
public class Singleton {
//volatile保证,当uniqueInstance变量被初始化成Singleton实例时,多个线程可以正确处理uniqueInstance变量
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getInstance() {
//检查实例,如果不存在,就进入同步代码块
if (uniqueInstance == null) {
//只有第一次才彻底执行这里的代码
synchronized(Singleton.class) {
//进入同步代码块后,再检查一次,如果仍是null,才创建实例
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
3.4 静态内部类实现方式(也是一种懒加载方式)
public class SingletonDemo04 {
private static class SingletonClassInstance {
private static final SingletonDemo04 instance = new SingletonDemo04();
}
public static SingletonDemo04 getInstance() {
return SingletonClassInstance.instance;
}
private SingletonDemo04() {
}
}
要点:
- 外部类没有static属性,则不会像饿汉式那样立即加载对象。
- 只有真正调用getInstance(),才会加载静态内部类。加载类时是线程 安全的。 instance是static final
类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性. – 兼备了并发高效调用和延迟加载的优势!
3.5 使用枚举实现单例模式
public enum SingletonDemo05 {
/**
* 定义一个枚举的元素,它就代表了Singleton的一个实例。
*/
INSTANCE;
/**
* 单例可以有自己的操作
*/
public void singletonOperation(){
//功能处理
}
}
使用方法:
public static void main(String[] args) {
SingletonDemo05 sd = SingletonDemo05.INSTANCE;
SingletonDemo05 sd2 = SingletonDemo05.INSTANCE;
System.out.println(sd==sd2);
}
- 优点: 实现简单:枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!
- 缺点:无延迟加载
四.总结
主要:
• 饿汉式(线程安全,调用效率高。 但是,不能延时加载。)
• 懒汉式(线程安全,调用效率不高。 但是,可以延时加载。)
其他:
• 静态内部类式(线程安全,调用效率高。 但是,可以延时加载)
• 枚举单例(线程安全,调用效率高,不能延时加载)