创建型模式--单例模式

 


单例模式:

单例模式分两种:

饿汉式:类加载时就会导致该单实例对象被创建

饿汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会被创建

饿汉式

1:静态成员变量方式

复制代码
//饿汉式:静态成员变量
public class Singleton {

    //私有构造方法
    private Singleton() {

    }

    //在本类中创建本类对象
    private static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }
}

public class Client {

    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance.equals(instance2));
    }
}

//true
复制代码

2: 静态代码块

复制代码
//饿汉式:静态代码块
public class Singleton {

    //私有构造方法
    private Singleton() {
    }

    //在静态代码块中进行赋值
    private static Singleton instance;

    static {
        instance = new Singleton();
    }

    public static Singleton getInstance() {
        return instance;
    }

}

public class Client {

    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance.equals(instance2));
    }
}

//true
复制代码

懒汉式:

线程不安全与加锁后的线程安全

复制代码
//懒汉式1:线程不安全的
public class Singleton {

    //私有构造方法
    private Singleton() {
    }

    //声明Singleton类型的变量instance
    private static Singleton instance;

  //可以在这里加synchronized 使线程安全,但是还是不够完美
public static Singleton getInstance() { if (instance == null) { //线程1等待,线程2获取到cpu的执行权,也会进入到该判断里 instance = new Singleton(); } return instance; } } public class Client { public static void main(String[] args) { Singleton instance = Singleton.getInstance(); Singleton instance2 = Singleton.getInstance(); System.out.println(instance.equals(instance2)); } } //false 加锁之后 就是同一个对象了,但是线程不安全,所以就是还可能会false
复制代码

双重检查方式:

双重检查模式是一种非常好的单例实现模式,解决了单例,性能,线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针的问题,出现问题的原因是JVM在实例化对象时会进行优化和指令重排序操作.

要解决双重检查锁模式带来的空指针异常的问题,只需要使用volatile关键字,volatile关键字可以保证可见性和有序性

复制代码
public class Singleton {

    private Singleton() {
    }

  //使用volatile解决空指针问题
private static volatile Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } public class Client { public static void main(String[] args) { Singleton instance = Singleton.getInstance(); Singleton instance2 = Singleton.getInstance(); System.out.println(instance.equals(instance2)); } } //true
复制代码

静态内部类方式

静态内部类单例模式中实例由内部类创建,由于JVM在加载外部类的时候,是不会加载静态内部类的,只有内部类的属性和方法被调用时才会被加载,并初始化其静态属性.静态属性由于被static修饰,保证只被实例化一次,并且还保证实例化顺序.

复制代码
public class Singleton {

    private Singleton() {
    }

    private static class SingletonHolder{
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

public class Client {

    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance.equals(instance2));
    }
}

//true
复制代码

在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间浪费

枚举方式

枚举方式实现单例模式是极力推荐的,因为枚举类型是线程安全的,并且只会装载一次,设计者充分利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式

复制代码
public enum Singleton {

    INSTANCE;
}

public class Client {

    public static void main(String[] args) {
        Singleton instance = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance.equals(instance2));
    }
}

//true
复制代码

属于饿汉式

单例模式的破坏方法:实现序列化

复制代码
public class Singleton implements Serializable {

    private Singleton() {
    }

    private static class SingletonHolder{
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

    public static void main(String[] args) throws Exception {
        //write();
        read();
        read();
    }

    public static void read() throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\pizza\\Desktop\\a.txt"));
        Singleton singleton = (Singleton) ois.readObject();
        ois.close();
        System.out.println(singleton);
    }

    public static void write() throws Exception {
        //获取Singleton对象
        Singleton instance = Singleton.getInstance();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\pizza\\Desktop\\a.txt"));
        oos.writeObject(instance);
        oos.close();
    }
复制代码

 

 

问题的解决:

在Singleton 中加readResolve() 方法,在反序列化是被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象

 

 单例模式的破坏:反射方式

复制代码
public class Singleton implements Serializable {

    private Singleton() {
    }

    private static class SingletonHolder{
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

    public static void main(String[] args) throws Exception {
        //获取字节码对象
        Class<Singleton> singletonClass = Singleton.class;
        Constructor<Singleton> declaredConstructor = singletonClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Singleton singleton = declaredConstructor.newInstance();
        Singleton singleton2 = declaredConstructor.newInstance();
        System.out.println(singleton == singleton2);
    }

//false 表示不是同一个对象,说明破坏了单例模式
复制代码

JDK源码解析-Runtime类

Runtime类就是单例设计模式

1,通过源代码查看使用的是哪种单例模式----是通过饿汉式来实现单例的

 

 

 

复制代码
public class RuntimeDemo {

    public static void main(String[] args) throws IOException {
        //获取Runtime对象
        Runtime runtime = Runtime.getRuntime();
        //调用runtime的方法exec
        Process process = runtime.exec("ipconfig");
        //获取输入流的方法
        InputStream inputStream = process.getInputStream();
        byte[] arr = new byte[1021 * 1024 * 100];
        //读取数据
        int len = inputStream.read(arr);
        System.out.println(new String(arr, 0, len, "GBK"));
    }
}
复制代码

 

posted @   着迷JAVA  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示