单例模式学习理解整理

本文详细介绍了Java中的单例模式实现,包括饿汉式单例(线程安全但资源效率低)、懒汉式单例(线程安全且资源利用率高,使用双检锁避免线程安全问题)以及枚举实现的单例(天然线程安全,防止反射和序列化破坏)。文章还通过代码示例展示了如何防止反射和序列化攻击单例,并探讨了不同实现的优缺点。
摘要由CSDN通过智能技术生成

饿汉式单例  

多线程安全,调用时对象已经创建所以快

缺点:资源效率不高

/**
 * 饿汉式单例
 */
public class Hungry {

    private Hungry(){
    }

    private static final Hungry hungry = new Hungry();

    public static Hungry getInstance(){
        return  hungry;
    }

}

懒汉式单例改良  

资源利用率高

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class LazyMan  implements Serializable{

    // 防止反序列化获取多个对象的漏洞。
    // 无论是实现Serializable接口,或是Externalizable接口,当从I/O流中读取对象时,readResolve()方法都会被调用到。
    // 实际上就是用readResolve()中返回的对象直接替换在反序列化过程中创建的对象。
    private Object readResolve() throws ObjectStreamException {
        return lazyMan;
    }



    //private static boolean  flag = false;

    private LazyMan(){
        synchronized (LazyMan.class){
            if(lazyMan !=null){
                throw new RuntimeException("不要试图用反射破坏单例");
            }
        }
    }

    private volatile   static  LazyMan lazyMan;

    //双检锁处理多线程不安全问题 双检锁的懒汉式单例 也叫DCL懒汉式
    public static LazyMan getInsance(){
        if(lazyMan ==null){
            synchronized (LazyMan.class){
                if(lazyMan ==null){
                    //1 分配内存
                    // 2执行构造方法,构建对象
                    //3对象赋值
                    //多线程情况下 123步骤可能会出现不按顺序执行,故懒汉单例模式在多线程不安全
                    lazyMan =  new LazyMan();
                }
            }

        }

        return lazyMan;
    }


    private static LazyMan getSerializedSingleton(LazyMan singleton){
        File file = new File("singleton");
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        LazyMan newSingleton = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream(file));
            oos.writeObject(singleton);
            oos.flush();
            ois = new ObjectInputStream(new FileInputStream(file));
            newSingleton = (LazyMan) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            if (ois != null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (oos != null){
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(file.exists()){
                file.delete();
            }
        }
        return newSingleton;
    }


    public static void main(String[] args) throws Exception {
     /*   LazyMan insance1 = LazyMan.getInsance();
        LazyMan insance2 = LazyMan.getInsance();
        System.out.println(insance1.hashCode());
        System.out.println(insance2.hashCode());

        //利用反射破解
        Constructor<? extends LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyMan insance3 = declaredConstructor.newInstance();
        System.out.println(insance3.hashCode());*/

        //双newInstance还是可以破坏
        Constructor<? extends LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyMan insance5 = declaredConstructor.newInstance();
        LazyMan insance6 = declaredConstructor.newInstance();
        System.out.println(insance5.hashCode());
        System.out.println(insance6.hashCode());

        Field[] declaredFields = LazyMan.class.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField.toString());
        }

        //利用序列化破坏单例
        //Write Obj to file
        //通过单例模式获取对象
        LazyMan insance = LazyMan.getInsance();
        //通过反序列化获取对象
        LazyMan deserializedSingleton = LazyMan.getSerializedSingleton(insance);
        System.out.println("hashCode of singleton:"+insance);
        System.out.println("hashCode of deserializedSingleton:"+deserializedSingleton);
        System.out.println(insance == deserializedSingleton);


    }

}

枚举类

防止反射,序列化影响,且多线程安全

import java.lang.reflect.Constructor;
import java.util.Arrays;

public enum Day {
    MONDAY("星期一", 1),
    TUESDAY("星期二", 2),
    WEDNESDAY("星期三", 3),
    THURSDAY("星期四", 4),
    FRIDAY("星期五", 5),
    SATURDAY("星期六", 6),
    SUNDAY("星期日", 7);//记住要用分号结束

    private String desc;//文字描述
    private Integer code; //对应的代码

    /**
     * 私有构造,防止被外部调用
     *
     * @param desc
     */
    private Day(String desc,Integer code) {
        this.desc = desc;
        this.code = code;
    }

    /**
     * 定义方法,返回描述,跟常规类的定义没区别
     *
     * @return
     */
    public String getDesc() {
        return desc;
    }

    /**
     * 定义方法,返回代码,跟常规类的定义没区别
     *
     * @return
     */
    public int getCode() {
        return code;
    }

    public static void main(String[] args) throws Exception {
        for (Day day : Day.values()) {
            System.out.println("name:" + day.name() +
                    ",desc:" + day.getDesc());
        }

        System.out.println(" is monday? " + isMonday(Day.MONDAY));

        Day monday = Day.MONDAY;
        Day tuesday = Day.MONDAY;
        System.out.println("args = " + monday.equals(tuesday));
        Constructor<Day> declaredConstructor = Day.class.getDeclaredConstructor(String.class,int.class,String.class,Integer.class);
        // Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
        //jad.exe反编译查看后 发现 枚举类,自带有
  //        private Day(String s, int i, String desc, Integer code)
  //      {
   //         super(s, i);
  //          this.desc = desc;
  //          this.code = code;
  //      }
        declaredConstructor.setAccessible(true);
        Day day = declaredConstructor.newInstance();
        System.out.println("args = " + monday.equals(day));

    }

    static boolean isMonday(Day day){
        if(Day.MONDAY.equals(day)){
            return  true;
        }
        return false;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值