设计模式简介
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。
单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式 ,确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例模式是创建型模式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
1、饿汉式
是否 Lazy 初始化:否
是否多线程安全:是
实现难度:易
描述:这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
标准饿汉模式,通过私有构造器,防止外部进行实例创建;通过属性在类加载时实例化对象,提供全局访问方法取得实例。利用代码的执行先后顺序,在线程还没有出现前就完成了实例化。
// 饿汉(勤汉)单例模式
public class Singleton01 {
// 2. 自己产生一个对象
private static Singleton01 singleton = new Singleton01();
private int num;
// 1. 构造器私有化
private Singleton01() {
num = (int) (Math.random() * 100 + 1);
}
// 3. 书写一个静态方法可以获取到这个对象
public static Singleton01 getInstance() {
return singleton;
}
public void print() {
System.out.println("输出了这个随机数:" + num);
}
}
测试类
public class App {
public static void a() {
Singleton01 s1 = Singleton01.getInstance();
Singleton01 s2 = Singleton01.getInstance();
System.out.println(s1 == s2);
s1.print();
s2.print();
}
public static void main(String[] args) {
a();
}
}
测试结果
饿汉式单例模式在类加载的时候就立即初始化,并且创建单例对象。它绝对线程安全,在线程还没出现以前就实例化了,不可能存在访问安全问题。
静态代码块机制,实现单例模式。
public class HungryStaticSingleton {
//静态志方式饿汉式单例,
private static final Singleton singleton ;
static {
singleton = new Singleton();
}
/**
* 私有构造
*/
private Singleton() {}
//取实例方法
public static Singleton getInstance() {
return singleton;
}
}
饿汉式单例模式适用于单例对象较少的情况。这样写可以保证绝对线程安全、执行效率比较高。但是它的缺点也很明显,就是所有对象类加载的时候就实例化。这样一来,如果系统中有大批量的单例对象存在,那系统初始化是就会导致大量的内存浪费。也就是说不管对象用与不用都占着空间,浪费了内存
2、懒汉式,线程不安全
是否 Lazy 初始化:是
是否多线程安全:否
实现难度:易
描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
// 懒汉模式--线程不安全
public class Singleton02 {
// 目前没有值 null
private static Singleton02 singleton;
//别人就不能产生你的对象
private Singleton02() {
}
public static Singleton02 getInstance() {
if (singleton == null) {
singleton = new Singleton02();
}
return singleton;
}
}
3、双检锁/双重校验锁(DCL,即 double-checked locking)
JDK 版本:JDK1.5 起
是否 Lazy 初始化:是
是否多线程安全:是
实现难度:较复杂
描述:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
getInstance() 的性能对应用程序很关键。
// 懒汉模式----线程安全
public class Singleton03 {
private static Singleton03 singleton;
private Singleton03() {
}
private static Object obj = new Object();
public static Singleton03 getInstance() {
if (singleton == null) { //这个if是为了防止因为加锁而出现得到线程阻塞
synchronized (Singleton03.class) {
if (singleton == null) { //这个是判断防止singleton3已经存在而继续创建对象
singleton = new Singleton03();
}
}
}
return singleton;
}
}
4、枚举
JDK 版本:JDK1.5 起
是否 Lazy 初始化:否
是否多线程安全:是
实现难度:易
描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。
public enum Singleton04 {
// 这里就是Singleton04的一个对象
INSTANCE;
public void a(){
System.out.println("aaaaa");
}
}