目录
一、什么是设计模式
1、设计模式是程序员在面对同类软件工程设计问题所总结出来的经验,模式不是代码,而是某类问题的通用解决方案,设计模式代表了最佳的实践。
2、设计模式的本质提高软件的维护性,通用性和扩展性,并降低软件的复杂度。
3、设计模式并不局限于某种语言,高级语言都有设计模式。
二、设计模式的类型
1、创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
2、结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
3、行为型模式:模板方法模式、命令模式、访问者模式、迭代模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、责任链模式。
三、单例模式
类的单例模式,就是采取一定的方法保证整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
代表例子:在Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session对象,但是SessionFactory是重量级的,一般情况下一个项目中只需要一个SessionFactory就行了,这里就会使用到单例模式。
四、单例模式的八种方式
1、饿汉式(静态常量)
2、饿汉式(静态代码块)
3、懒汉式(线程不安全)
4、懒汉式(线程安全,同步方法)
5、懒汉式(线程安全,同步代码块)
6、双重检查
7、静态内部类
8、枚举
4.1、饿汉式(静态常量)
1、构造器私有化(防止new)
2、类的内部创建对象
3、向外暴露一个静态的公共方法
示例参考代码:
public class SingletonDemo {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
//判断两个对象是否相同
System.out.println(singleton1 == singleton2); //true
//比较哈希值
System.out.println("singleton1.hashCode() = " + singleton1.hashCode());
System.out.println("singleton2.hashCode() = " + singleton2.hashCode());
}
}
//使用静态变量完成饿汉式
class Singleton{
//将构造器私有化(防止new)
private Singleton(){
}
//在本类内部创建对象
private final static Singleton sing = new Singleton();
//对外提供一个公有的静态方法,返回实例对象
public static Singleton getSingleton(){
return sing;
}
}
1、写法简单,在类装载的时候就完成实例化,避免了线程同步问题。
2、在类装载的时候就完成实例化,没有达到lazy Loading(懒加载)的效果。如果从始至终都没有使用过这个实例,就会造成内存的浪费。
3、基于classloder机制避免了多线程同步问题,但是实例化对象在类装载时就实例化,在单例模式中通常都调用getInstance方法,但是导致类装载的原因很多,不能确定是其他的方式(其他静态方法)导致类装载,在初始化时没有达到lazy loading的效果。
4、这种单例模式可以用,但是可能造成内存浪费。
4.2、饿汉式(静态代码块)
示例参考代码:
public class SingletonDemo {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
//判断两个对象是否相同
System.out.println(singleton1 == singleton2); //true
//比较哈希值
System.out.println("singleton1.hashCode() = " + singleton1.hashCode());
System.out.println("singleton2.hashCode() = " + singleton2.hashCode());
}
}
//使用静态代码块完成饿汉式
class Singleton{
//将构造器私有化(防止new)
private Singleton(){
}
//定义一个静态变量
private static Singleton sing ;
//在静态代码块中,完成创建对象
static {
sing = new Singleton();
}
//对外提供一个公有的静态方法,返回实例对象
public static Singleton getSingleton(){
return sing;
}
}
1、只是将类的实例化的过程放到了静态代码块中,也是类装载的时候,就执行静态代码块中的代码,初始化类的实例。
2、这种单例模式可以用,但是可能造成内存浪费。
4.3、懒汉式(线程不安全)
示例参考代码:
public class SingletonDemo2 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
//判断两个对象是否相同
System.out.println(singleton1 == singleton2); //true
//比较哈希值
System.out.println("singleton1.hashCode() = " + singleton1.hashCode());
System.out.println("singleton2.hashCode() = " + singleton2.hashCode());
}
}
//使用懒汉式
class Singleton{
private static Singleton singleton;
private Singleton(){
}
//提供一个对外的公共的静态方法
public static Singleton getSingleton(){
if (singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
1、起到了Lazy Loading的效果,但只能在单线程下使用。
2、当在多线程下,当一个线程进入了if判断语句块时,还没有来的及往下执行,另一个线程也通过这个判断语句,这时就会产生多个实例,在多线程环境下不可以使用这种方式。
3、在实际开发中,不要使用这种方式。
4.4、懒汉式(线程安全,同步方法)
示例参考代码:
public class SingletonDemo2 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
//判断两个对象是否相同
System.out.println(singleton1 == singleton2); //true
//比较哈希值
System.out.println("singleton1.hashCode() = " + singleton1.hashCode());
System.out.println("singleton2.hashCode() = " + singleton2.hashCode());
}
}
//使用懒汉式
class Singleton{
private static Singleton singleton;
private Singleton(){
}
//提供一个对外的公共的静态方法,加入同步代码块,解决线程安全问题。
public static synchronized Singleton getSingleton(){
if (singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
1、解决了线程不安全的问题
2、方法进行同步效率太低,每一个线程在想获得类的实例时,执行getSingleton()方法都要进行同步,而这个方法只需要执行一次就可以了,后面想要获得该类的实例对象直接return就可以了。
3、在实际开发使用中,不推荐使用。
4.5、懒汉式(线程不安全,同步代码块)
示例参考代码:
public class SingletonDemo2 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
//判断两个对象是否相同
System.out.println(singleton1 == singleton2); //true
//比较哈希值
System.out.println("singleton1.hashCode() = " + singleton1.hashCode());
System.out.println("singleton2.hashCode() = " + singleton2.hashCode());
}
}
//使用懒汉式
class Singleton{
private static Singleton singleton;
private Singleton(){
}
//提供一个对外的公共的静态方法,加入同步代码块,解决线程安全问题。
public static Singleton getSingleton(){
if (singleton == null){
synchronized (Singleton.class){
singleton = new Singleton();
}
}
return singleton;
}
}
1、对懒汉式(线程安全,同步方法)进行改进,因为前面使用同步方法效率太低,改为同步产生实例化的代码块。
2、这种同步代码块在实际中并不能起到线程同步的作用,只要线程进入if判断语句,没有及时往下执行,另一个线程也通过这个判断语句,就会产生多个实例。
3、在实际开发中,不要使用。
4.6、双重检查
实例参考代码:
public class SingletonDemo2 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
//判断两个对象是否相同
System.out.println(singleton1 == singleton2); //true
//比较哈希值
System.out.println("singleton1.hashCode() = " + singleton1.hashCode());
System.out.println("singleton2.hashCode() = " + singleton2.hashCode());
}
}
//使用懒汉式
class Singleton{
private static volatile Singleton singleton;
private Singleton(){
}
//提供一个对外的公共的静态方法,加入双重检查,解决线程安全和懒加载问题。
public static Singleton getSingleton(){
if (singleton == null){
synchronized (Singleton.class){
if (singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
1、Double-Check概念是多线程开发中经常使用到的,在代码中我们会进行两次if判断检查,保证了线程安全。
2、实例化代码只会执行一次,后面再次访问时,判断if直接return实例化对象,避免了反复进行方法同步。
3、双重检查保证了线程安全,延迟加载,效率较高,在实际开发中,推荐使用。
4.7、静态内部类
示例参考代码:
public class SingletonDemo2 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
//判断两个对象是否相同
System.out.println(singleton1 == singleton2); //true
//比较哈希值
System.out.println("singleton1.hashCode() = " + singleton1.hashCode());
System.out.println("singleton2.hashCode() = " + singleton2.hashCode());
}
}
//使用静态内部类
class Singleton{
//构造器私有化
private Singleton(){
}
private static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();
}
//提供一个对外的公共的静态方法,调用静态内部类返回SingletonInstance.INSTANCE
public static Singleton getSingleton(){
return SingletonInstance.INSTANCE;
}
}
1、采用了类装载的机制来保证初始化实例时只有一个线程。
2、静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用对外提供的静态方法,才会装载SingletonInstancel类,完成对象实例化。
3、类的静态属性只会在第一次加载类的时候初始化,利用了JVM保证了线程安全性,在类进行初始化时,别的线程没法进入的。
4、避免了线程不安全,利用静态内部类特点实现了延迟加载,效率高,推荐使用。
4.8、枚举
示例参考代码:
public class SingletonDemo3 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.SINGLETON;
Singleton singleton2 = Singleton.SINGLETON;
System.out.println(singleton1 == singleton2);
System.out.println("singleton1.hashCode() = " + singleton1.hashCode());
System.out.println("singleton2.hashCode() = " + singleton2.hashCode());
singleton1.getSingleton();
}
}
enum Singleton{
SINGLETON;
public void getSingleton(){
System.out.println("对象创建成功");
}
}
1、借助JDK1.5中添加枚举的方式来实现单例模式,不仅可以避免多线程同步的问题也可以防止反序列化重新创建新的对象。
2、是Effective Java作者Josh Bloch提倡的方式,推荐使用。
五、JDK源码中的单例模式
在Java的Runtime类中
六、单例模式的注意事项
1、单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
2、当实例化一个单例类的时候,必须使用相对应的获取对象的方法,而不是使用new。
3、使用需求:需要频繁的进行创建和销毁的对象,创建对象耗时过多或耗费资源过多(重量级对象),但又经常用到的对象,工具类对象、频繁访问数据库文件或文件的对象(数据源、session工厂)。