zoukankan      html  css  js  c++  java
  • 单例模式学习笔记

    1 单例模式(Singleton Pattern)介绍

     1.1 单例模式介绍

      定义:确保某一类只有一个实例,而且自行实例化并向整个系统提供这个实例。

      实现:通过使用private的构造函数确保了在一个应用中只产生一个实例,并且是自行实例化。

      示例代码:

        例1-1

    •  1 public class Singleton01 {
       2     //2、创建一个对象,在类加载的时候初始
       3     private static final Singleton01 singleton01 = new Singleton01();
       4     //1、私有化构造方法,现在以new的方式创建多个对象
       5     private Singleton01(){
       6 
       7     }
       8     //3、对外提供一个静态方法,以获取实例对象
       9     public static Singleton01 getSingleton(){
      10         return singleton01;
      11     }
      12     //类中的其他方法
      13     public void doSomething(){
      14     }
      15 }

      1.2 单例模式的应用

      1.2.1 优点

    • 单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁创建、销毁时,而且创建或销毁时性能无法优化,单例模式的优势就非常明显。
    • 单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
    • 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。

      1.2.2 缺点

    • 单例模式一般没有接口,扩展困难。
    • 对测试不利,如果单例模式没有完成,是不能进行测试的。

      1.2.3 使用场景

    • 要求生成唯一序列号的环境。
    • 在整个项目中需要一个共享访问点或共享数据,例如一个web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保存计数器的值,并确保是线程安全的。
    • 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源。
    • 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static的方式)。
    • windows的任务管理器,工厂模式中的工厂....

    2 单例模式的实现方式

      2.1 饿汉式(线程安全,调用效率高[因为不用加锁],但是不能延时加载),如例1-1 。

      2.2 懒汉式 

      当使用懒加载时(如例2-2-1),在高并发环境下,存在线程安全问题(图2-2-1),可能出现同时创建多个对象,需要对线程进行加锁(例2-2-2),此称为懒汉式,资源利用效率高,实现了懒加载,但是并发调用效率低,由于每次都要加载所有浪费系统资源。

      例2-2-1:

    •  1 public class Singleton02 {
       2     //2、声明一个私有的静态变量
       3     private static Singleton02 singleton02 ;
       4     //1、私有化构造方法,现在以new的方式创建多个对象
       5     private Singleton02(){
       6     }
       7     //3、对外提供一个静态方法,以获取实例对象
       8     public static  Singleton02 getSingleton(){
       9         //判断singleton02是否为空,为空赋值
      10         if (singleton02 == null){
      11             singleton02 = new Singleton02();
      12         }
      13         return singleton02;
      14     }
      15 }

      图2-2-1 

      

      例2-2-2:

    •  1 public class Singleton02 {
       2     //2、声明一个私有的静态变量
       3     private static Singleton02 singleton02 ;
       4     //1、私有化构造方法,现在以new的方式创建多个对象
       5     private Singleton02(){
       6     }
       7     //3、对外提供一个静态方法,以获取实例对象
       8     public static synchronized Singleton02 getSingleton(){
       9         //判断singleton02是否为空,为空赋值
      10         if (singleton02 == null){
      11             singleton02 = new Singleton02();
      12         }
      13         return singleton02;
      14     }
      15 }

      2.3 双重检测锁(由于编译器优化原因,和jvm底层模型问题,偶尔会出现问题,不建议使用)

      例2-3-1

    •  1 public class Singleton03 {
       2     //2、声明一个私有的静态成员变量
       3     private static Singleton03 singleton03;
       4     //1、私有化构造方法,现在以new的方式创建多个对象
       5     private Singleton03(){
       6     }
       7     //3、对外提供一个静态方法,以获取实例对象
       8     public static Singleton03 getSingleton(){
       9         //判断singleton03是否需要加锁
      10         if (singleton03 == null){
      11             synchronized (Singleton03.class){
      12                 if (singleton03 == null){
      13                     singleton03 = new Singleton03();
      14                 }
      15             }
      16         }
      17         return singleton03;
      18     }

      图2-3-1

       

      2.4 静态内部类(常用)

        外部类没有static,所以静态内部类不会再外部类加载的时候被初始化,所以实现了懒加载

        线程安全,因为实例对象是在静态内部类加载的时候创建,所以天然是单例的。

      例2-4-1 

    •  1 public class Singleton04 {
       2 
       3     //1、私有化构造方法,现在以new的方式创建多个对象
       4     private Singleton04(){
       5     }
       6     //2、创建一个静态内部类
       7     private static class SingletonInstance{
       8         //静态内部类加载的时候生成单例对象
       9         public static Singleton04 singleton04 = new Singleton04();
      10     }
      11     //3、对外提供一个静态方法,以获取实例对象
      12     public static Singleton04 getSingleton(){
      13         //当调用该方法是,静态内部类才会被加载,对象才会new出来
      14         return SingletonInstance.singleton04;
      15     }
      16 }

      2.5 枚举(线程安全,天然就单例的,能避免反射和反序列化带来的问题,但是不能懒加载)

      例2-5-1

    • 1 public enum Singleton05 {
      2     SINGLETON_05;
      3     public void doSomething(){
      4     }
      5 }
       1 public class Client {
       2     public static void main(String[] args) {
       3         Singleton05 singleton05_01 = Singleton05.SINGLETON_05;
       4         Singleton05 singleton05_02 = Singleton05.SINGLETON_05;
       5         Singleton05 singleton05_03 = Singleton05.SINGLETON_05;
       6         System.out.println(singleton05_01);//SINGLETON_05
       7         System.out.println(singleton05_02);//SINGLETON_05
       8         System.out.println(singleton05_03);//SINGLETON_05
       9         singleton05_01.doSomething();
      10     }
      11 }    

    3 防止反射破解单例

      3.1反射破解单例示例:

      例3-1-1

     1 //破解类
     2 public class Client {
     3     public static void main(String[] args) throws Exception {
     4 
     5         Singleton01 singleton1 = Singleton01.getSingleton();
     6         Singleton01 singleton2 = Singleton01.getSingleton();
     7         System.out.println(singleton1 == singleton2); //true
     8 
     9         //暴力反射破解单例
    10         Class<Singleton01> clazz = (Class<Singleton01>) Class.forName("com.pri.singleton_a.Singleton01");
    11         Constructor<Singleton01> constructor = clazz.getDeclaredConstructor(null);
    12         constructor.setAccessible(true);
    13         Singleton01 singleton3 = constructor.newInstance(null);
    14 
    15         System.out.println(singleton1 == singleton3); //false
    16     }
    17 }
    18 
    19 //单例类
    20 public class Singleton01 {
    21     //2、创建一个对象,在类加载的时候初始
    22     private static final Singleton01 singleton01 = new Singleton01();
    23     //1、私有化构造方法,现在以new的方式创建多个对象
    24     private Singleton01(){
    25 
    26     }
    27     //3、对外提供一个静态方法,以获取实例对象
    28     public static Singleton01 getSingleton(){
    29         return singleton01;
    30     }
    31     //类中的其他方法
    32     public void doSomething(){
    33     }
    34 }

      3.2 防止反射破解单例

      在单例空参构造中添加判断,如

      例3-2-1:

    •  1 public class Singleton01 {
       2     //2、创建一个对象,在类加载的时候初始
       3     private static final Singleton01 singleton01 = new Singleton01();
       4     //1、私有化构造方法,现在以new的方式创建多个对象
       5     private Singleton01(){
       6         if (singleton01 != null){
       7             throw new RuntimeException("已有实例,不能再调用此方法实例化");
       8         }
       9     }
      10     //3、对外提供一个静态方法,以获取实例对象
      11     public static Singleton01 getSingleton(){
      12         return singleton01;
      13     }
      14     //类中的其他方法
      15     public void doSomething(){
      16     }
      17 }
      18 
      19 //运行结果 :Caused by: java.lang.RuntimeException: 已有实例,不能再调用此方法实例化

    4 防止反序列化破解单例

      4.1 反序列化破解单例

      例4-1-1

    •  1 //破解类
       2 public class Client {
       3     public static void main(String[] args) throws Exception {
       4 
       5         Singleton01 singleton1 = Singleton01.getSingleton();
       6         Singleton01 singleton2 = Singleton01.getSingleton();
       7         System.out.println(singleton1 == singleton2); //true
       8 
       9         /*//1、将对象序列化到文件
      10         FileOutputStream out = new FileOutputStream("singleton.txt");
      11         ObjectOutputStream oos = new ObjectOutputStream(out);
      12         oos.writeObject(singleton1);
      13 
      14         oos.close();
      15         out.close();*/
      16 
      17         //2、从文件中读取对象(反序列化)
      18         FileInputStream input = new FileInputStream(new File("singleton.txt"));
      19 
      20         ObjectInputStream ois = new ObjectInputStream(input);
      21         Singleton01 singleton3 = (Singleton01) ois.readObject();
      22 
      23         System.out.println(singleton1 == singleton3); //false      
      24     }
      25 }
      26 
      27 //单例类
      28 public class Singleton01 implements Serializable {
      29     //2、创建一个对象,在类加载的时候初始
      30     private static final Singleton01 singleton01 = new Singleton01();
      31     //1、私有化构造方法,现在以new的方式创建多个对象
      32     private Singleton01(){
      33         if (singleton01 != null){
      34             throw new RuntimeException("已有实例,不能再调用此方法实例化");
      35         }
      36     }
      37     //3、对外提供一个静态方法,以获取实例对象
      38     public static Singleton01 getSingleton(){
      39         return singleton01;
      40     }
      41     //类中的其他方法
      42     public void doSomething(){
      43     }
      44 }

      4.2 防止反序列化破解单例

      在单例类中添加一个readResolve()方法,如例4-2-1.

      例4-2-1

    •  1 public class Singleton01 implements Serializable {
       2     //2、创建一个对象,在类加载的时候初始
       3     private static final Singleton01 singleton01 = new Singleton01();
       4     //1、私有化构造方法,现在以new的方式创建多个对象
       5     private Singleton01(){
       6         if (singleton01 != null){
       7             throw new RuntimeException("已有实例,不能再调用此方法实例化");
       8         }
       9     }
      10     //3、对外提供一个静态方法,以获取实例对象
      11     public static Singleton01 getSingleton(){
      12         return singleton01;
      13     }
      14     //类中的其他方法
      15     public void doSomething(){
      16     }
      17 
      18     //反序列化时,直接return singleton01,不生成对象
      19     private Object readResolve() throws ObjectStreamException{
      20         return singleton01;
      21     }
      22 }
  • 相关阅读:
    JavaScript循环 — for、for/in、while、do/while
    Git
    js根据日期获取所在周
    nodejs安装 Later version of Node.js is already installed. Setup will now exit 及 node与npm版本不符
    sqlserver 2014 json
    根据官方数据制作中国省市区数据库
    kubernetes系列③:集群升级-实践(参照官方文档)
    kubernetes系列:服务外部访问集中管理组件-ingress-nginx
    kubernetes系列-部署篇:Kubernetes的包管理工具-helm
    kubernetes系列-部署篇:使用kubeadm初始化一个高可用的Kubernetes集群
  • 原文地址:https://www.cnblogs.com/gdwkong/p/8412622.html
Copyright © 2011-2022 走看看