设计模式(1)——单例模式
目录一. 单例模式的含义二. 单例模式的四种常见方式2.1 饿汉式经典加载方式2.2 懒汉式加载方式2.3 静态内部类方式加载2.4 枚举类加载方式一. 单例模式的含义单例简单来说就是单个实例是一种常见的软件的设计模式。一个类只实例化自己的一个实例并且不允许再创建多余的实例在对外提供访问时访问的都是同一个实例对象。二. 单例模式的四种常见方式2.1 饿汉式经典加载方式很好理解饿的见到什么都吃在单例模式中指不管用到用不到的资源都统统加载到内存中。优点实现简单实用性强且不存在线程安全问题是我们最常使用的一种实现方式缺点不管用到与否类加载时就完成实例化统统加载到内存用不用都要加载感觉有点浪费和多余占用内存空间代码实现如下这种方法实现单例模式的核心原理是JVM会且仅会将一个类加载一次到内存中去加载一次 Mgr01 类那么 INSTANCE 对象就被创建出来了public class Mgr01 { # 创建一个静态私有化 INSTANCE 对象 private static final Mgr01 INSTANCE new Mgr01(); # 私有化构造器不允许其他人再创建新的类对象 private Mgr01(){} # 编写get方法调用get方法即可返回已经创建好的 INSTANCE 对象 public static Mgr01 getInstance(){ return INSTANCE; } # 此处main方法为测试方法与设计模式无关 public static void main(String[] args) { Mgr01 m1 new Mgr01().getInstance(); Mgr01 m2 new Mgr01().getInstance(); System.out.println(m1 m2); } }我们来运行一下 main 方法就会发现 m1 对象和 m2 对象是相等的如下图所示2.2 懒汉式加载方式对比饿汉式加载懒汉式最大的特点就是懒我不加载我在需要用到的时候才去进行加载。public class Mgr02 { // 创建一个静态 Mgr02 对象 INSTANCE,但不进行实例化, // 并且不许使用 volatile 修饰禁止指令重排否则可能会出错设计知识点较深暂不解说 public static volatile Mgr02 INSTANCE; // 私有化无参构造禁止其他人访问创建新实例 private Mgr02(){} // 在 get 方法中进行对象的实例化并采用双重校验实现线程安全 public static Mgr02 getInstance(){ // 第一次检查如果 INSTANCE 不为空直接返回 INSTANCE 对象 if (INSTANCE null){ // 双重检查再次判断避免出现线程安全问题 synchronized (Mgr02.class){ if (INSTANCE null){ INSTANCE new Mgr02(); } } } return INSTANCE; } // 此处为测试 main方法测试线程安全 public static void main(String[] args) { // 循环一百次创建一百个线程 for (int i 0; i 100; i) { // 匿名内部类的方法创建线程对象 new Thread(new Runnable() { Override public void run() { // 打印获取到的实例对象的哈希码如果一样则说明为同一个对象 System.out.println(Mgr02.getInstance().hashCode()); } }).start; } } }运行 main 方法我们可以在控制台发现打印的对象的哈希码是一样的说明这100个线程对象获取到的 INSTANCE 对象本质上都是同一个2.3 静态内部类方式加载静态内部类是通过JVM的方式保证线程安全JVM会保证每个类之加载一次但类中的静态内部类是不会被加载的只有当我们调用 get 方法时get 方法会返回给我们一个静态内部类的INSTANCE而INSTANCE在静态内部类中指代的就是外部类的实例对象非常神奇非常花哨并且可以做到线程安全任何人来访问get方法获取Mgr03对象的实例获取到的都是同一个INSTANCE。实现代码如下所示public class Mgr03 { // 私有化构造方法 private Mgr03(){} // 定义静态内部类静态内部类不会随着类的加载而加载 private static class Mgr03Helper{ private final static Mgr03 INSTANCE new Mgr03(); } // 定义静态 get 方法 public static Mgr03 getInstance(){ return Mgr03Helper.INSTANCE; } // 此处为 测试 main方法测试是否有线程安全问题 public static void main(String[] args) { for (int i 0; i 100; i) { new Thread(new Runnable() { Override public void run() { System.out.println(Mgr03.getInstance().hashCode()); } }).start(); } } }运行此方法如下图所示所有对向的哈希码都是一样的2.4 枚举类加载方式枚举类实现单例模式的方法更为简单我们只需要定义一个枚举类然后在枚举类中定义一个INSTANCE对象枚举类会在变量的前面默认添加 public static final所以不需要我们再额外添加// 定义一个枚举类 Mgr01 public enum Mgr04 { // 定义 INSTANCE 对象 INSTANCE; // 此处为 main 测试方法测试是否线程安全 public static void main(String[] args) { for (int i 0; i 100; i) { new Thread(new Runnable() { Override public void run() { System.out.println(Mgr04.INSTANCE.hashCode()); } }).start(); } } }运行 main 方法可以在控制台发现所有的对象的哈希码都是一样的是线程安全的