什么是设计模式
- 静态方法和属性的经典使用
- 设计模式实在大量的时间中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己在思考和摸索
什么是单例模式
单例(单个的实例)
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
单例模式有两种方式:
- 饿汉式(静态常量/静态代码块)
- 懒汉式(线程不安全/线程安全,同步方法/线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
单例模式应用案例
步骤如下:
- 构造器私有化 =>防止直接 new,导致多个实例
- 类的内部创建对象
- 向外暴漏一个静态的方法 getInstance
- 代码实现
枚举
饿汉式和懒汉式区别:
- 饿汉式是在类内部一开始初始化时就创建好实例,而懒汉式是需要用的时候才去创建实例
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题
- 饿汉式存在浪费资源的可能,当程序员一个对象实例都没有使用,那么饿汉式创建表的对象就浪费了,懒汉式是使用时才创建,不存在这个问题
- 在JavaSE标准类中,Java.lang.Runtime就是经典的单例模式
饿汉式(静态常量/静态代码块)
在开发中推荐使用,尤其是该类的实例会用到,避免了造成内存的浪费,如果该类的实例不用,则会造成内存的浪费。
//饿汉式 public class SingleTon01 { public static void main(String[] args) { //通过方法可以获取对象 GirlFriend instance = GirlFriend.getInstance(); System.out.println(instance.toString()); } } //有一个类,GirlFriend //只能有一个女朋友 class GirlFriend{ private String name; //为了能够在静态方法中,返回gf对象,需要将其修饰为static对象 private static GirlFriend gf = new GirlFriend("12"); //如何保障我们只能创建一个GirlFriend对象 //步骤(饿汉式) //1. 将构造器私有化 //2.在类的内部直接创建 //3.提供一个公共的static方法,返回gf对象 private GirlFriend(String name) { this.name = name; } public static GirlFriend getInstance(){ return gf; } @Override public String toString() { return "name:"+this.name; } }
饿汉式(静态代码块)
*/ public class Singleton{ private static Singleton singleton = new Singleton();//饿汉式,初始化时就创建好了实例 //代码块[使用了static代码块-->饿汉式(静态代码块)] static{ singleton = new Singleton(); } private Singleton(){}//构造器私有化,防止new,导致多个实例 public static Singleton getInstance(){//向外暴露一个静态的公共方法 getInstance return singleton; } }
饿汉式(静态常量) 和 饿汉式(静态代码块) 的优点、缺点:相同
优点:实现较为简单,在类加载时就完成了实例化,避免了多线程同步问题
缺点:在类加载时就完成了实例化(使类加载的情况有很多种,不一定是调用getInstance()方法使类进行加载的),没有达到懒加载的效果。如果程序从始至终未用到该实例,则造成了空间浪费
懒汉式(线程不安全/ 线程安全,同步方法 / 线程安全,同步代码块)
懒汉式(线程不安全)
package com.shedu.single; /** * 懒汉式的单例模式 */ public class SingleTen02 { public static void main(String[] args) { Cat instance = Cat.getInstance(); System.out.println(instance); } } //希望在程序运行过程中,只能创建一个对象 class Cat{ private String name; private static Cat cat; //步骤: //1.构造器私有化 //2.定义一个static静态属性对象 //3.提供一个public的static方法,可以返回一个Cat对象 //4.懒汉式,只有当用户使用getInstance时,才返回cat对象, //后面再次调用时,会返回上次创建的cat对象,从而保证了单例 private Cat(String name) { this.name = name; } public static Cat getInstance(){ if (cat == null) {//如果没有创建cat对象 cat = new Cat("小猫"); } return cat; } @Override public String toString() { return "Cat{" + "name='" + name + '\'' + '}'; } }
懒汉式(线程不安全) 优缺点:起到懒加载的效果,但是只适合在单线程下使用(开发中不推荐使用)
线程不安全原因:如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式
懒汉式(线程安全,同步方法)
public class Singleton{ private Singleton(){}//构造器私有化,防止new,导致多个实例 private static Singleton singleton; //同步方法,synchronized直接加在方法上 public static synchronized Singleton getInstance(){//向外暴露一个静态的公共方法 getInstance if(singleton == null){ singleton = new Singleton(); } return singleton; } }
懒汉式(线程安全,同步方法) 优缺点:起到懒加载的效果,线程安全,但是调用效率低(开发中不推荐使用)
懒汉式(线程安全,同步代码块)
public class Singleton{
private Singleton(){}//构造器私有化,防止new,导致多个实例
private static Singleton singleton;
public static Singleton getInstance(){//向外暴露一个静态的公共方法 getInstance
if(singleton == null){
//同步代码块,synchronized是单独作为代码块使用
synchronized (Singleton.class){
singleton = new Singleton();
}
}
return singleton;
}
懒汉式(线程安全,同步代码块) 优缺点:起到懒加载的效果,但是只适合在单线程下使用(开发中不推荐使用)
- 线程不安全原因:和 懒汉式(线程不安全)一样。
双重检查
public class Singleton{
private Singleton(){}//构造器私有化,防止new,导致多个实例
private static volatile Singleton singleton;
public static Singleton getInstance(){//向外暴露一个静态的公共方法 getInstance
//第一层检查
if(singleton == null){
//同步代码块
synchronized (Singleton.class){
//第二层检查
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
双重检查优缺点:解决了同步代码块方式的线程安全问题。
其实就是上面的 懒汉式(线程安全,同步代码块)的优化改良版。
静态内部类
public class Singleton{ private Singleton(){}//构造器私有化,防止new,导致多个实例 //静态内部类,在其内部以静态常量的方式实例化对象 private static class SingletonInstance{ private static final Singleton singleton = new Singleton();//常量静态属性,实例化对象[初始化] } public static Singleton getInstance(){//向外暴露一个静态的公共方法 getInstance return SingletonInstance.singleton; } }
- 静态内部类优缺点:(利用了jvm的两个特点,起到了懒加载、线程安全的作用)
懒加载:利用了jvm装载的特点:当外部类加载的时候,内部静态类不会被加载,从而保证了懒加载。
线程安全:当类在进行初始化的时候,别的线程是无法进入的。通过类的静态属性只会在第一次加载类的时候初始化,保证了线程安全。
1.当外部类 Singleton被装载时,静态内部类 SingletonInstance不会被立即装载,实现懒加载
2.当外部类 Singleton调用getInstance()时,静态内部类 SingletonInstance只被装载一次,在初始化静态内部类SingletonInstance的静态常量属性 singleton,保证了线程安全。
- 静态内部类优缺点:(利用了jvm的两个特点,起到了懒加载、线程安全的作用)
枚举方式
enum Singleton{ INSTANCE; public void method(){ // 操作方法 } }
枚举方式优缺点: 线程安全,效率高,还可防止反序列化重新创建新的对象.
四、单例的使用场景
需要频繁的进行创建和销毁的对象、
创建对象时耗时过多或 耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)