单例模式

单例模式的前提:1.有私有的构造方法(为了代码中不可以直接new),2.全局唯一的访问点

懒汉式单例:懒汉的双重加锁机制

package com.java8.java8.singleton;

public class LazySingleton {
    public static void main(String[] args) {

        System.out.println(LazySingleton.getInstance());
    }


    // 这里要加上内存屏障,防止getInstance过程中指令重排的发生
    private volatile static LazySingleton modle = null;//声明对象,不实例化


    //构造函数私有化
    private LazySingleton()  {

    }

    //向外提供访问该类对象的方法
// 需要加锁要不会有线程安全问题,自己体会不细说了
    public static LazySingleton getInstance(){
        if (modle == null)
            synchronized (LazySingleton.class){
                modle = new LazySingleton();
            }

        return mo	dle;

    }

}

饿汉式:这个饿汉式还可以升级下,这个类如果有很多静态变量,或者静态方法,他们被调用的时候,这个单例都会被创建了,粒度太粗了。

package com.java8.java8.singleton;

public class HungrySingleton {

    public static void main(String[] args) {
        
        System.out.println(HungrySingleton.NAME);
    }

    
    static{
        System.out.println("static块创建");
    }

    private static final String NAME = "张工";
    // 这个对象是在类加载过程中创建的,静态代码块创建了这个对象也就创建了
    private static HungrySingleton modle = new HungrySingleton();

    //构造函数私有化
    private HungrySingleton(){

    }

    //向外声明访问该类对象的方法
    public static HungrySingleton getInstance() {
        return modle;
    }
}

细粒度的懒汉式单例也叫内部类单例:

package com.java8.java8.singleton;

public class HungrySingleton2 {

    public static void main(String[] args) {

        //  调用外面的静态变量,主要是为了看下内部类的单例会不会打印
        System.out.println(HungrySingleton2.NAME);

        // 调用内部类的静态变量,开始创建单例对象
        //System.out.println(InnerSingleton.getInstance());

    }


    static{
        System.out.println("外面static块创建");
    }

    private static final String NAME = "张工";

    // 创建这个内部类就是为了把粗的单例变细,什么时候第一次调用getInstance什么时候在创建对象
    static class InnerSingleton{
        static{
            System.out.println("内部类static块创建");
        }

        // 这个对象是在类加载过程中创建的,静态代码块创建了这个对象也就创建了
        private static InnerSingleton modle = new InnerSingleton();

        //构造函数私有化
        private InnerSingleton(){

        }

        //向外声明访问该类对象的方法
        public static InnerSingleton getInstance() {
            return modle;
        }
    }


}

枚举单例

public enum  EnumSingleton {
    INSTANCE;
    public EnumSingleton getInstance(){
        return INSTANCE;
    }
}

完整的枚举单例

package com.java8.java8.singleton;

public class CompleteEnumSingleton {
    //私有化构造函数
    private CompleteEnumSingleton(){ }

    //定义一个静态枚举类
    static enum SingletonEnum{
        //创建一个枚举对象,该对象天生为单例
        INSTANCE;
        private CompleteEnumSingleton user;
        //私有化枚举的构造函数
        private SingletonEnum(){
            user=new CompleteEnumSingleton();
        }
        public CompleteEnumSingleton getInstnce(){
            return user;
        }
    }

    //对外暴露一个获取User对象的静态方法
    public static CompleteEnumSingleton getInstance(){
        return SingletonEnum.INSTANCE.getInstnce();
    }
}

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

以上3种方式创建了单例对象,现在讲解下怎么来破坏单例

1.反射

package com.java8.java8.singleton.destory;

import com.java8.java8.singleton.HungrySingleton;

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

public class DestorySingleton {

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 通过反射创建对象
        Constructor<HungrySingleton> declareConstructor = HungrySingleton.class.getDeclaredConstructor();
        declareConstructor.setAccessible(true);
        HungrySingleton hungrySingleton1 = declareConstructor.newInstance();

        //正常创建对象
        HungrySingleton hungrySingleton2 = HungrySingleton.getInstance();
        System.out.println(hungrySingleton1 == hungrySingleton2);
    }
}

上面的单例被破坏了,但是我们可以在私有方法上加上检查。这样反射创建对象时候就检查住了。

    //构造函数私有化
    private HungrySingleton(){
        if( modle != null){
            throw new RuntimeException("对象已经创建了");
        }
    }

2.序列化

// 把这个对象序列化

package com.java8.java8.singleton.serizerDestory;

import java.io.Serializable;

public class HungrySingleton implements Serializable {

    public static void main(String[] args) {

        System.out.println(HungrySingleton.NAME);
    }


    static{
        System.out.println("static块创建");
    }

    private static final String NAME = "张工";
    // 这个对象是在类加载过程中创建的,静态代码块创建了这个对象也就创建了
    private static HungrySingleton modle = new HungrySingleton();

    //构造函数私有化
    private HungrySingleton(){
        if( modle != null){
            throw new RuntimeException("对象已经创建了");
        }
    }

    //向外声明访问该类对象的方法
    public static HungrySingleton getInstance() {
        return modle;
    }

    // 这句是解决序列化反序列化对象破环单例用的
    private Object readResolve(){
        return modle;
    }
}

// 序列化对象和反序列化对象后和原来单例对象比较

package com.java8.java8.singleton.serizerDestory;


import java.io.*;

public class HungrySingletonSerizerTest {

    public static void main(String[] args) {


        HungrySingleton hungrySingleton = HungrySingleton.getInstance();
        // 把对象序列化后保存在文件中
//        try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test"))) {
//            objectOutputStream.writeObject(hungrySingleton);
//        } catch (FileNotFoundException e) {
//            e.printStackTrace();
//        } catch (IOException e) {
//            e.printStackTrace();
//        }

        // 把上面的文件反序列化成对象
        HungrySingleton hungrySingleton2 = null;
        try (ObjectInputStream objectOutputStream = new ObjectInputStream(new FileInputStream("test"))) {
            hungrySingleton2 = (HungrySingleton) objectOutputStream.readObject();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        System.out.println(hungrySingleton2.toString());
        System.out.println(hungrySingleton.toString());

        // 比较单例创建出来的对象和文件饭序列化出来的对象是否相同比较,结果为false,可见反序列化破化了单例
        // 解决办法在对象中readResolve()  指定数据源
//        private Object readResolve(){
//            return modle;
//        }
        System.out.println(hungrySingleton2 == hungrySingleton);
    }
}

解决办法在代码中写了

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值