Seven ways to create single column mode and use reflection and serialization to crack single column

1. Description

1. Basic concepts of singleton

There will only be one instance object in the current Jvm

2. Singleton application scenarios

1. The configuration file defined in the project 2.
Servlet object is singleton by default
3. Thread pool, database connection pool
4. Bean object in Spring is singleton by default
5. Implement website counter
6. Jvm built-in cache framework (define singleton HashMap )
7. Define enum constant information

3/Singleton advantages and disadvantages

Advantages: Can save the current heap memory, do not need to frequently New objects, and can be accessed quickly.
Disadvantages: When multiple threads access the same singleton object, there may be thread safety issues.

Two, 7 ways to create single-column mode

  • If you add reflection to create + serialization create , for the nine kinds to create a way (to break a single column re-created)
  • If you add in the constructor to create , for the 10 kinds of way to create
  • The main purpose of the constructor using private modification is to prevent the external direct use of the new object to initialize and change the class, becoming multiple columns

1. Lazy man (when concurrently-there are security flaws)

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. Lazy + synchronized lock mechanism (there is no security flaw, but the performance is low)

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. Lazy man + double inspection lock (no defect + best performance)

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. Hungry Chinese style (commonly used and static variables, the shortcomings are not used and occupy memory resources)

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. Static method block creation (not commonly used)

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. Static internal class creation (commonly used in 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. Created by enumeration (absolutely thread-safe)

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);
    }
}

Three, crack the single column

1. Reflection cracking

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);
    }
}

Insert picture description here

2. Serialization cracking

/**
 * 序列化破解单列 (当类添加了 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);

    }
}

Insert picture description here

3. Try to crack the enumeration by reflection (can't crack)

/**
  * 方式破解单列(无法破解,直接抛出异常)
  * @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();
    }
}

Insert picture description here

4. Try to serialize and crack the enumeration (unable to crack)


/**
 * 序列化破解枚举(先天性安全,无法破解)
 * @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);
    }
}

Insert picture description here

  • Some of the above content comes from the Ant Classroom http://www.mayikt.com/

  • Personal open source project (universal background management system) –> https://gitee.com/wslxm/spring-boot-plus2 , you can check it out if you like

  • This is the end of this article. If you find it useful, please like or pay attention to it. We will continue to update more content from time to time... Thank you for watching!

Guess you like

Origin blog.csdn.net/qq_41463655/article/details/108416482