Java单例模式

目录

一.实现单例模式

1.懒汉

2.饿汉

3.静态内部类

4.双重校验锁

5.枚举

二.破坏单例模式

1.反射破坏单例模式

2.反序列化破坏单例模式

三.避免破坏单例模式

1.避免反射破坏单例模式

2.避免反序列化破坏单例模式


一.实现单例模式

单例模式(Singleton): 确保一个类只有一个实例,并提供一个全局访问点.可以保证对象的唯一性,并方便对象的管理和控制.

实现思路:

  1. 如果要实现一个单例,首先要把类的构造函数私有化,否则就可以随时通过构造函数创建对象
  2. 构造函数私有化之后,需要提供一个方法,可以初始化对象,并且保证只能初始化一个对象,还需要考虑线程安全问题.
  3. 实现方法主要有5种:懒汉、饿汉、静态内部类、双重校验锁、枚举.

1.懒汉

懒汉:就是需要对象的时候才去创建,可以避免提前创建浪费资源

//单例模式--懒汉
public class Demo {

    private static Demo demo;

    private Demo(){}
  
    public static synchronized Demo getDemo(){
        if (demo == null) {
            demo = new Demo();
        }
        return demo;
    }

}

2.饿汉

饿汉:就是在类刚一开始初始化的时候就把对象创建处理

//单例模式--饿汉
public class Demo1 implements Serializable {

    private static Demo1 demo1 = new Demo1();

    private Demo1(){}

    public static Demo1 getDemo1(){
        return demo1;
    }

}

3.静态内部类

//单例模式--静态内部类
public class Demo2 {
    private static class Demo2Holder{
        public static final Demo2 DEMO_2= new Demo2();
    }
    private Demo2(){}
    public static final Demo2 getDemo2(){
        return Demo2Holder.DEMO_2;
    }
}

4.双重校验锁

//单例模式--双重校验锁
public class Demo4 {
    private volatile static Demo4 demo4;
    private Demo4(){}
    public static Demo4 getDemo4(){
        if (demo4==null) {
            synchronized (Demo4.class){
                if (demo4 == null) {
                    demo4 = new Demo4();
                }
            }
        }
        return demo4;
    }
}

5.枚举

//单例模式--枚举
public enum Demo3 {
    DEMO_3;
    public void method(){}
}


二.破坏单例模式

单例模式主要是通过把一个类的构造方法私有化,来避免重复创建多个对象的。那么,想要破坏单例,只要想办法能够执行到这个私有的构造方法就行了。—般来说做法有使用反射及使用反序列化都可以破坏单例。

1.反射破坏单例模式

//通过反射破坏单例模式
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    Demo demo = Demo.getDemo();
    //通过反射获取构造函数
    Constructor<Demo> declaredConstructor = Demo.class.getDeclaredConstructor();
    //将构造函数设为可访问类型
    declaredConstructor.setAccessible(true);
    //调用构造函数创建对象
    Demo demo1 = declaredConstructor.newInstance();
    //比较对象
    System.out.println(demo == demo1);// 输出为flase
}

2.反序列化破坏单例模式

序列化底层会通过反射调用无参构造方法新建对象

//通过反序列化破坏单例模式
public static void main(String[] args)throws Exception{
    //写入对象
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("duixiang.txt"));
    oos.writeObject(Demo1.getDemo1());

    //读取对象
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("duixiang.txt"));
    Demo1 demo1 = (Demo1) ois.readObject();
    
    System.out.println(demo1 == Demo1.getDemo1());//输出false
    oos.close();
    ois.close();
}


三.避免破坏单例模式

1.避免反射破坏单例模式

反射是调用默认的无参构造函数创建出来的,只需要我们改造下构造函数,使其在反射调用的时候识别出来对象是不是被创建过就行了:

//单例模式--懒汉
public class Demo {

    private static Demo demo;

    private Demo(){
        if (demo != null) {
            throw new RuntimeException("对象已经创建过了");
        }
    }
    public static synchronized Demo getDemo(){
        if (demo == null) {
            demo = new Demo();
        }
        return demo;
    }
}

2.避免反序列化破坏单例模式

只需要在类中定义readResolve就可以避免单例模式被破坏

//单例模式--饿汉
public class Demo1 implements Serializable {

    private static Demo1 demo1 = new Demo1();

    private Demo1(){}

    public static Demo1 getDemo1(){
        return demo1;
    }

    private Object readResolve(){
        return demo1;
    }

    //通过反序列化破坏单例模式
    public static void main(String[] args)throws Exception{
        //写入对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("duixiang.txt"));
        oos.writeObject(Demo1.getDemo1());

        //读取对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("duixiang.txt"));
        Demo1 demo1 = (Demo1) ois.readObject();

        System.out.println(demo1 == Demo1.getDemo1());//输出为true
        oos.close();
        ois.close();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值