设计模式学习(二)

系列文章目录

设计模式学习(一)

文章目录

 


前言

 

学习单例模式

一、单例模式是什么?

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

二、饿汉式

package FOG23;

//饿汉式单例
public class Hungry {

    private Hungry(){

    }

    private final static Hungry HUNGRY=new Hungry();

    public static Hungry getInstance(){
        return HUNGRY;
    }
}

问题:该类静态创建了对象,浪费了内存

三、懒汉式

package FOG23;
//懒汉式单例
public class Lazy {

    private Lazy(){

    }
    private  static Lazy LAZY;
    //单线程ok,多线程不行,要加锁
    //双重检测,DCL懒汉式
    private static  Lazy getInstance(){
        if (LAZY==null){
            synchronized (Lazy.class){
                if (LAZY==null){
                    LAZY=new Lazy();
                }
            }
        }
        return LAZY;
    }
}

 

问题:该类单线程ok,多线程不行,要加锁。LAZY=new Lazy()在cpu中不是一个原子操作。(1.分配内存2,执行构造方法,初始化3,把对象指向这个空间)可能会出现132这个顺序。

四、静态内部类

package FOG23;
//静态内部类
public class Holder {

    private Holder(){

    }
    public static class InnerClass{
        private static final Holder HOLDER=new Holder();
    }
    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }
}

五、安全性

以上的三种都不安全,都可以通过java反射机制获得构造器,获得新的实例对象。

已懒汉式为例:

package FOG23;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

//懒汉式单例
public class Lazy {

    private Lazy(){

    }
    private  static Lazy LAZY;
    //单线程ok,多线程不行,要加锁
    //双重检测,DCL懒汉式
    private static  Lazy getInstance(){
        if (LAZY==null){
            synchronized (Lazy.class){
                if (LAZY==null){
                    LAZY=new Lazy();
                }
            }
        }
        return LAZY;
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Lazy instance = Lazy.getInstance();
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);//关闭安全检测,操作私有属性
        Lazy instance1 = declaredConstructor.newInstance();

        System.out.println(instance);
        System.out.println(instance1);
        //instance!=instance1; 不是单例了
    }
}

处理这个反射机制:在无参构造中继续加入一个检测

private Lazy(){
        synchronized (Lazy.class){
            if (LAZY!=null){
                throw new RuntimeException("无法通过反射实例!");
            }
        }

    }

问题:无需通过

Lazy instance = Lazy.getInstance();创建对象。
 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //Lazy instance = Lazy.getInstance();
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);//关闭安全检测,操作私有属性
        Lazy instance1 = declaredConstructor.newInstance();
        Lazy instance2 = declaredConstructor.newInstance();
        System.out.println(instance2);
        System.out.println(instance1);
        //instance!=instance1; 不是单例了
    }

 

进一步解决这个问题:在类中加一个静态的标志:来进行判断是否通过反射来进行实例化

private static boolean Tip=false;
    private Lazy(){
        synchronized (Lazy.class){
            if (Tip==false){
                Tip=true;
            }else {
                throw new RuntimeException("无法通过反射实例!");
            }
        }

    }

问题:当知道这个标志符后依旧能将类的实例:

   public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        //Lazy instance = Lazy.getInstance();
        Field tip = Lazy.class.getDeclaredField("Tip");
        tip.setAccessible(true);
        
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);//关闭安全检测,操作私有属性
        Lazy instance1 = declaredConstructor.newInstance();
        tip.set(instance1,false);
        Lazy instance2 = declaredConstructor.newInstance();
        System.out.println(instance2);
        System.out.println(instance1);
        //instance!=instance1; 不是单例了
    }

 

五、如何解决(枚举)

创建一个枚举类:

//enum 本身是个类
public enum  enumStrings {

    INSTANCE;

    public enumStrings getInstance(){
        return INSTANCE;
    }

}

根据java反射来实现一下实例化

class Test{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        enumStrings instance =  enumStrings.INSTANCE; //获得enumString实例
        //通过反射获得默认的无参构造,
        Constructor<enumStrings> declaredConstructor = enumStrings.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        enumStrings newInstance = declaredConstructor.newInstance();
        //正常结果应该是:IllegalArgumentException("Cannot reflectively create enum objects"); 不能通过反射创建
        //而结果是:Exception in thread "main" java.lang.NoSuchMethodException: FOG23.enumStrings.<init>()
        //没有这个空参的构造方法
        System.out.println(instance);
        System.out.println(newInstance);
    }
}

通过反编译:发现在enumStrings.class中出现了一个有参构造:


    public enumStrings(String s,int i){
        super(s,i);
    }

然后在mian方法中修改:

class Test{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        enumStrings instance =  enumStrings.INSTANCE; //获得enumString实例
        //通过反射获得默认的无参构造,
        Constructor<enumStrings> declaredConstructor = enumStrings.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        enumStrings newInstance = declaredConstructor.newInstance();
        //结果为:IllegalArgumentException("Cannot reflectively create enum objects"); 不能通过反射创建
        System.out.println(instance);
        System.out.println(newInstance);
    }


}

 


总结

反射无法破解enum的单例

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值