Sete maneiras de criar o modo de coluna única e usar reflexão e serialização para quebrar a coluna única

1. Descrição

1. Conceitos básicos de singleton

Haverá apenas um objeto de instância no Jvm atual

2. Cenários de aplicação singleton

1. O arquivo de configuração definido no projeto 2.
Objeto servlet é singleton por padrão
3. Pool de threads, pool de conexão de banco de dados
4. O objeto Bean no Spring é singleton por padrão
5. Implementar contador de site
6. Estrutura de cache integrada Jvm (definir singleton HashMap)
7. Definir informações constantes enum

3 / Vantagens e desvantagens do Singleton

Vantagens: Pode salvar a memória heap atual, não precisa de novos objetos com frequência e pode ser acessado rapidamente.
Desvantagens: Quando vários threads acessam o mesmo objeto singleton, pode haver problemas de segurança de thread.

Duas, 7 maneiras de criar o modo de coluna única

  • Se você adicionar reflexão para criar + serialização criar , para os nove tipos de criar uma maneira (para quebrar uma única coluna recriada)
  • Se você adicionar o construtor para criar , para os 10 tipos de maneira de criar
  • O objetivo principal do construtor usando modificação privada é evitar o uso direto externo do novo objeto para inicializar e alterar a classe, tornando-se múltiplas colunas

1. Homem preguiçoso (quando simultaneamente, há falhas de segurança)

package com.xijia;

import java.io.Serializable;

/**
 * 单例模式(懒汉式)
 * @author wangsong
 * @mail [email protected]
 * @date 2020/9/5 0005 9:49 
 * @version 1.0.0
 */
public class Singleton01 implements Serializable {

    private static Singleton01 singleton01 = null;

    /**
     * 私有化,禁止new 改对象
     */
    private Singleton01() {
    }

    /**
     * 此创建方法  缺陷: 如果两个线程同时进入该 getInstance()方法,会破坏数据初始化的数据
     * 如果: singleton01为计数器或火车票
     * ----a执行 初始化 singleton01 = 1
     * ----b执行 初始化 singleton01 = 1
     * 实际结果应该为2,但实际结果为1 ,有兴趣可自行模拟
     * @return
     */
    public static Singleton01 getInstance() {
        if (singleton01 == null) {
            singleton01 = new Singleton01();
        }
        return singleton01;
    }

    public static void main(String[] args) {
        // 初始化
        Singleton01 instance1 = Singleton01.getInstance();
        // 直接获取,不初始化, 但是遇到instance1和instance2 的调用同时进入if 条件,部分业务数据将不正确(如计数器,火车票等)
        Singleton01 instance2 = Singleton01.getInstance();
        System.out.println(instance1 == instance2);
    }

}

2. Mecanismo de bloqueio lento + sincronizado (não há falha de segurança, mas o desempenho é baixo)

package com.xijia;

/**
 * 单列模式(懒汉式 + synchronized 锁机制)
 * @author wangsong
 * @mail [email protected]
 * @date 2020/9/5 0005 9:58
 * @version 1.0.0
 */
public class Singleton02 {

    private static Singleton02 singleton02;

    private Singleton02() {
    }

    /**
     * 初始化 singleton02, 改方式避免了案例一 计数器重复初始化导致结果错误的问题,但存在一定的缺陷
     * 缺陷:
     * 初始化singleton02 无缺陷
     * 但 获取singleton02 数据时出现缺陷, 当我们获取singleton02时,两个线程同时获取singleton02时,必须排队获取singleton02,这显然是会严重影响性能的
     * @return
     */
    public static synchronized Singleton02 getInstance() {
        if (singleton02 == null) {
            singleton02 = new Singleton02();
        }
        return singleton02;
    }

    public static void main(String[] args) {
        // 初始化singleton02
        Singleton02 instance1 = Singleton02.getInstance();
        // 直接获取,不初始化
        Singleton02 instance2 = Singleton02.getInstance();
        System.out.println(instance1 == instance2);
    }
}

3. Homem preguiçoso + trava de inspeção dupla (sem defeito + melhor desempenho)

package com.xijia;


/**
  * 单列模式(懒汉式 + 双重检验锁)
  * @author wangsong
  * @mail  [email protected]
  * @date  2020/9/5 0005 10:06
  * @version 1.0.0
  */
public class Singleton03 {
    private static Singleton03 singleton03;

    private Singleton03() {
    }

    /**
     * 双重检验锁初始化 singleton03
     * 无缺陷
     * ----  修复案例2的 获取数据需要排队的问题,获取数据直接获取,
     * ----- 如果遇到多个线程同时进入 if ,加锁排队,第一个进入的线程初始化了对象,下一个排队的人进入后判断发现对象已经初始化了,就不重新初始化了
     * @return
     */`在这里插入代码片`
    public static Singleton03 getInstance() {
        // 上锁(创建该对象) 第一次判断
        if (singleton03 == null) {
            synchronized (Singleton03.class) {
                //第二次判断
                if (singleton03 == null) {
                    singleton03 = new Singleton03();
                }
            }
        }
        return singleton03;
    }


    public static void main(String[] args) {
        Singleton03 instance1= Singleton03.getInstance();
        Singleton03 instance2= Singleton03.getInstance();
        System.out.println(instance1==instance2);
    }
}

4. Estilo chinês com fome (variáveis ​​estáticas e comumente usadas, as deficiências não são usadas e ocupam recursos de memória)

package com.xijia;


/**
 * 饿汉式
 * @author wangsong
 * @mail [email protected]
 * @date 2020/9/5 0005 10:12
 * @version 1.0.0
 */
public class Singleton04 {

    /**
     * 当class文件被加载的时候就创建该对象,常用于创建常量
     */
    public static final Singleton04 singleton04 = new Singleton04();

    private Singleton04() {
    }

    public static void main(String[] args) {
        System.out.println(Singleton04.singleton04 == Singleton04.singleton04);
    }

}

5. Criação de bloco de método estático (não comumente usado)

package com.xijia;


/**
 * 静态方法区
 * @author wangsong
 * @mail [email protected]
 * @date 2020/9/5 0005 10:14
 * @version 1.0.0
 */
public class Singleton05 {


    private static Singleton05 singleton05 = null;

    private Singleton05() {
    }

    /**
     * 只会初始化一次(或常用的 init方法)
     */
    static {
        System.out.println("当前class被加载");
        singleton05 = new Singleton05();
    }

    public static synchronized Singleton05 getInstance() {
        return Singleton05.singleton05;
    }


    public static void main(String[] args) {
        System.out.println(Singleton05.getInstance() == Singleton05.getInstance());
    }
}

6. Criação de classe interna estática (comumente usada no Android)

package com.xijia;


/**
  * 静态内部类
  * @author wangsong
  * @mail  [email protected]
  * @date  2020/9/5 0005 10:16
  * @version 1.0.0
  */
public class Singleton06 {

    public Singleton06() {
    }

    /**
     *  静态内部类 (安卓常用)
     */
    private static class SingletonHolder {
        private static final Singleton06 singleton06 = new Singleton06();
    }

    /**
     * 获取静态内部类的 singleton06
     * @return
     */
    public static Singleton06 getInstance() {
        return SingletonHolder.singleton06;
    }


    public static void main(String[] args) {
        Singleton06 instance1 = Singleton06.getInstance();
        Singleton06 instance2 = Singleton06.getInstance();
        System.out.println(instance1 == instance2);
    }
}

7. Criação de enumeração (segurança absoluta de thread)

package com.xijia;


/**
 * 枚举方式创建
 * @author wangsong
 * @date 2020/9/5 0005 10:21
 * @return
 * @version 1.0.0
 */
public enum Singleton07 {
    INSTANCE;

    public void addUser() {
        System.out.println("我是枚举,我先天性安全");
    }


    Singleton07() {
        System.out.println(">>>Singleton07无参构造函数执行<<");
    }


    public static void main(String[] args) {
        // 只会执行一次构造函数
        System.out.println(Singleton07.INSTANCE == Singleton07.INSTANCE);
    }
}

Três, quebre a única coluna

1. Rachadura de reflexão

public class Test001 {
    public static void main(String[] args) throws Exception {

        // 使用反射机制创建我们的对象
        Class<?> aClass = Class.forName("com.xijia.Singleton01");
        //  getDeclaredConstructor();获取当前类(不包含父类),getConstructor 所有的  包含父类构造函数
        Constructor<?> constructor = aClass.getDeclaredConstructor();
        constructor.setAccessible(true);
        // 走无参构造函数 反射创建对象成功
        Singleton01 instance1 = (Singleton01) constructor.newInstance();
        Singleton01 instance2 = Singleton01.getInstance();
        /**
         * 将输出false, 单列对象 Singleton01 被重复创建, singleton01 在静态区的值被重新初始化,原数据将被破坏
         */
        System.out.println(instance1 == instance2);
    }
}

Insira a descrição da imagem aqui

2. Fissuração de serialização

/**
 * 序列化破解单列 (当类添加了 implements Serializable 的,都将可以破解单列)
 * @author wangsong
 * @date 2020/9/5 0005 10:26
 * @return
 * @version 1.0.0
 */
public class Test002 {
    public static void main(String[] args) throws Exception {
        // 1.需要将该对象序列化到本地存放
        FileOutputStream fos =  new FileOutputStream("d:/code/user.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        Singleton01 instance1 = Singleton01.getInstance();
        oos.writeObject(instance1);
        oos.close();
        fos.close();
        //2.从硬盘中反序列化对象到内存中
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/code/user.txt"));
        Singleton01 instance2 = (Singleton01) ois.readObject();
        /**
         * 将输出false, 单列对象 Singleton01 被重复创建, singleton01 在静态区的值被重新初始化,原数据将被破坏
         */
        System.out.println(instance1==instance2);

    }
}

Insira a descrição da imagem aqui

3. Tente quebrar a enumeração por reflexão (não consigo quebrar)

/**
  * 方式破解单列(无法破解,直接抛出异常)
  * @author wangsong
  * @mail  [email protected]
  * @date  2020/9/5 0005 10:33
  * @version 1.0.0
  */
public class Test004 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {

        Class<?> aClass = Class.forName("com.xijia.Singleton07");
        /**
         * 直接抛出,Exception in thread "main" java.lang.InstantiationException: com.xijia.Singleton07 异常
         */
        Object o = aClass.newInstance();
    }
}

Insira a descrição da imagem aqui

4. Tente serializar e quebrar a enumeração (incapaz de quebrar)


/**
 * 序列化破解枚举(先天性安全,无法破解)
 * @author wangsong
 * @date 2020/9/5 0005 10:30
 * @return
 * @version 1.0.0
 */
public class Test003 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 1.将对象序列化存入到本地文件中
        FileOutputStream fos = new FileOutputStream("d:/code/a.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        Singleton07 instance1 = Singleton07.INSTANCE;
        oos.writeObject(Singleton07.INSTANCE);
        oos.close();
        fos.close();

        //2.从硬盘中反序列化对象到内存中
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/code/a.txt"));
        Singleton07 instance2 = (Singleton07) ois.readObject();
        /**
         * 输出true, 枚举类没有被重新创建,原数据绝对安全
         */
        System.out.println(instance1 == instance2);
    }
}

Insira a descrição da imagem aqui

  • Parte do conteúdo acima vem da Ant Classroom http://www.mayikt.com/

  • Projeto pessoal de código aberto (sistema de gerenciamento universal de fundo) -> https://gitee.com/wslxm/spring-boot-plus2 , você pode conferir se quiser

  • Este é o fim deste artigo. Se você achar útil, dê um like ou preste atenção nele. Continuaremos a atualizar mais conteúdo de vez em quando ... Obrigado por assistir!

Acho que você gosta

Origin blog.csdn.net/qq_41463655/article/details/108416482
Recomendado
Clasificación