栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > Java

面试:单例模式

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

面试:单例模式

1.饿汉式 1.1介绍

饿汉式是相对于懒汉式来说的,懒汉式是第一次调用 getInstance() 方法时,才创建实例,而饿汉式则是不调用 getInstance() 方法,类初始化时,实例会被提前创建出来

// 1. 饿汉式
public class Singleton1 implements Serializable {

    // 构造私有
    private Singleton1() {
        System.out.println("private Singleton1()");
    }

    // 静态成员变量
    private static final Singleton1 INSTANCE = new Singleton1();

    // 公共静态方法
    public static Singleton1 getInstance() {
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}
public class TestSingleton {
    public static void main(String[] args) throws Exception {
        Singleton1.otherMethod();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        // 调用两次,看看是否是同一实例
        System.out.println(Singleton1.getInstance());
        System.out.println(Singleton1.getInstance());

    }
}

控制台输出:
private Singleton1()
otherMethod()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
com.ee.dd.mal.rest.Singleton1@782830e
com.ee.dd.mal.rest.Singleton1@782830e

查看控制台输出:

com.ee.dd.mal.rest.Singleton1@782830e
com.ee.dd.mal.rest.Singleton1@782830e

说明是同一对象;

调用静态方法 otherMethod(),可以触发 Singleton1 的初始化操作,类初始化操作时,会调用构造方法,单例对象会被创建,getInstance() 时,拿到的是已经创建好的对象;

1.2单例的破坏方法 1.2.1反射破坏单例
// 饿汉式
public class Singleton1 implements Serializable {

    // 构造私有
    private Singleton1() {
        System.out.println("private Singleton1()");
    }

    // 静态成员变量
    private static final Singleton1 INSTANCE = new Singleton1();

    // 公共静态方法
    public static Singleton1 getInstance() {
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}
    public static void main(String[] args) throws Exception {
        Singleton1.otherMethod();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        // 调用两次,看看是否是同一实例
        System.out.println(Singleton1.getInstance());
        System.out.println(Singleton1.getInstance());

        // 反射破坏单例
        reflection(Singleton1.class);
    }

    private static void reflection(Class clazz) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Constructor constructor = clazz.getDeclaredConstructor(); // 拿到一个无参的构造方法
        constructor.setAccessible(true); // 设置私有的构造方法,也可以被使用
        System.out.println("反射创建实例:" + constructor.newInstance()); // 调用构造方法的 newInstance() 也可以创建实例
    }

控制台输出:
private Singleton1()
otherMethod()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
com.qq.ww.mal.rest.Singleton1@782830e
com.qq.ww.mal.rest.Singleton1@782830e
private Singleton1()
反射创建实例:com.hbis.ttie.mal.rest.Singleton1@470e2030

可以看到,getInstance() 拿到一个对象,通过反射,构造方法的信息输出了出来,创建了另一个对象,一个类,两个对象,就不是单例了

如何预防?

修改代码

// 饿汉式
public class Singleton1 implements Serializable {

    // 构造私有
    private Singleton1() {
        if (INSTANCE != null) {
            throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private Singleton1()");
    }

    // 静态成员变量
    private static final Singleton1 INSTANCE = new Singleton1();

    // 公共静态方法
    public static Singleton1 getInstance() {
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}
    public static void main(String[] args) throws Exception {
        Singleton1.otherMethod();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        // 调用两次,看看是否是同一实例
        System.out.println(Singleton1.getInstance());
        System.out.println(Singleton1.getInstance());

        // 反射破坏单例
        reflection(Singleton1.class);
    }

    private static void reflection(Class clazz) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Constructor constructor = clazz.getDeclaredConstructor(); // 拿到一个无参的构造方法
        constructor.setAccessible(true); // 设置私有的构造方法,也可以被使用
        System.out.println("反射创建实例:" + constructor.newInstance()); // 调用构造方法的 newInstance() 也可以创建实例
    }

控制台输出:
private Singleton1()
otherMethod()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
com.qq.qq.mal.rest.Singleton1@782830e
com.qq.qq.mal.rest.Singleton1@782830e
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.qq.qq.mal.rest.TestSingleton.reflection(TestSingleton.java:25)
	at com.qq.qq.mal.rest.TestSingleton.main(TestSingleton.java:19)
Caused by: java.lang.RuntimeException: 单例对象不能重复创建
	at com.qq.qq.mal.rest.Singleton1.(Singleton1.java:11)
	... 6 more

发现反射调用的时候,报错了

1.2.2反序列化破坏单例

如果单例对象实现了 Serializable 接口,单例可以被破坏

// 饿汉式
public class Singleton1 implements Serializable {

    // 构造私有
    private Singleton1() {
        if (INSTANCE != null) {
            throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private Singleton1()");
    }

    // 静态成员变量
    private static final Singleton1 INSTANCE = new Singleton1();

    // 公共静态方法
    public static Singleton1 getInstance() {
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}
    public static void main(String[] args) throws Exception {
        Singleton1.otherMethod();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        // 调用两次,看看是否是同一实例
        System.out.println(Singleton1.getInstance());
        System.out.println(Singleton1.getInstance());

        // 反序列化破坏单例
        serializable(Singleton1.getInstance());
    }

    private static void serializable(Object instance) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(instance); // 变成字节流
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        System.out.println("反序列化创建实例:" + ois.readObject()); // 把字节流还原成对象,这是一个新的对象,并且不调用构造方法
    }

控制台输出:
private Singleton1()
otherMethod()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
com.qq.qq.mal.rest.Singleton1@782830e
com.qq.qq.mal.rest.Singleton1@782830e
反序列化创建实例:com.hbis.ttie.mal.rest.Singleton1@2280cdac

查看控制台输出,创建了两个对象

如何预防?

// 饿汉式
public class Singleton1 implements Serializable {

    // 构造私有
    private Singleton1() {
        if (INSTANCE != null) {
            throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private Singleton1()");
    }

    // 静态成员变量
    private static final Singleton1 INSTANCE = new Singleton1();

    // 公共静态方法
    public static Singleton1 getInstance() {
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }

    // 方法名是固定的
    // 在反序列化时,发现重写了 readResolve() ,那么会利用 readResolve() 的返回值,作为结果返回
    public Object readResolve() {
        return INSTANCE;
    }

}
    public static void main(String[] args) throws Exception {
        Singleton1.otherMethod();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        // 调用两次,看看是否是同一实例
        System.out.println(Singleton1.getInstance());
        System.out.println(Singleton1.getInstance());

        // 反序列化破坏单例
        serializable(Singleton1.getInstance());
    }

    private static void serializable(Object instance) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(instance); // 变成字节流
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        System.out.println("反序列化创建实例:" + ois.readObject()); // 把字节流还原成对象,这是一个新的对象,并且不调用构造方法
    }

控制台输出:
private Singleton1()
otherMethod()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
com.qq.qq.mal.rest.Singleton1@782830e
com.qq.qq.mal.rest.Singleton1@782830e
反序列化创建实例:com.hbis.ttie.mal.rest.Singleton1@782830e

查看控制台输出,发现是同一对象

1.2.3Unsafe 破坏单例
// 饿汉式
public class Singleton1 implements Serializable {

    // 构造私有
    private Singleton1() {
        if (INSTANCE != null) {
            throw new RuntimeException("单例对象不能重复创建");
        }
        System.out.println("private Singleton1()");
    }

    // 静态成员变量
    private static final Singleton1 INSTANCE = new Singleton1();

    // 公共静态方法
    public static Singleton1 getInstance() {
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }

    // 方法名是固定的
    // 在反序列化时,发现重写了 readResolve() ,那么会利用 readResolve() 的返回值,作为结果返回
    public Object readResolve() {
        return INSTANCE;
    }

}
    public static void main(String[] args) throws Exception {
        Singleton1.otherMethod();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        // 调用两次,看看是否是同一实例
        System.out.println(Singleton1.getInstance());
        System.out.println(Singleton1.getInstance());

        // Unsafe 破坏单例
        unsafe(Singleton1.class);
    }

    // unsafe是jdk的内置类,不能直接访问,
    private static void unsafe(Class clazz) throws InstantiationException {
        Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz); // 根据类型,创建一个实例,也不会调用构造方法
        System.out.println("Unsafe 创建实例:" + o);
    }

控制台输出:
private Singleton1()
otherMethod()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
com.qq.qq.mal.rest.Singleton1@782830e
com.qq.qq.mal.rest.Singleton1@782830e
Unsafe 创建实例:com.hbis.ttie.mal.rest.Singleton1@5fdef03a

查看控制台输出,发现创建了两个实例

还没找到预防方法

2.枚举饿汉式 2.1介绍
enum Sex {
    MALE, FEMALE;
}

光看代码不好理解,对其进行反编译,得到如下代码:

final class Sex extends Enum {
    public static final Sex MALE;
    public static final Sex FEMALE;

    private Sex(String name, int ordinal) {
        super(name, ordinal);
    }

    static {
        MALE = new Sex("MALE", 0);
        FEMALE = new Sex("FEMALE", 1);
        $VALUES = values();
    }

    private static final Sex[] $VALUES;

    private static Sex[] $values() {
        return new Sex[]{MALE, FEMALE};
    }

    public static Sex[] values() {
        return $VALUES.clone();
    }

    public static Sex valueOf(String value) {
        return Enum.valueOf(Sex.class, value);
    }
}

final:修饰,说明不能被继承,不能有子类;

Enum:枚举父类,不能在代码中直接写,是编译不通过的,继承关系是编译器在编译时加上的;

枚举类的饿汉式单例

// 枚举饿汉式
public enum Singleton2 {
    INSTANCE;

    // 以下代码,都是不必要的,为了测试方便而添加的

    // 构造方法,默认是 private 的,去掉以后,依然是私有的
    // idea也提示 Modifier 'private' is redundant for enum constructors 修饰符'private'对于enum构造函数是多余的
    // 主要是为了打印信息,看构造方法是否调用
    private Singleton2() {
        System.out.println("private Singleton2()");
    }

    // 打印枚举类时,把 hash 码也打印出来,能区分是否是同一对象,默认打印枚举的名字
    @Override
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    // 静态公共方法,获取单例
    public static Singleton2 getInstance() {
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}
    public static void main(String[] args) throws Exception {
        Singleton2.otherMethod();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        // 调用两次,看看是否是同一实例
        System.out.println(Singleton2.getInstance());
        System.out.println(Singleton2.getInstance());

    }

控制台输出:
private Singleton2()
otherMethod()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
com.qq.qq.mal.rest.Singleton2@782830e
com.qq.qq.mal.rest.Singleton2@782830e

调用静态方法 otherMethod(),可以触发 Singleton2 的初始化操作,类初始化操作时,会调用构造方法,单例对象会被创建,getInstance() 时,拿到的是已经创建好的对象;

2.2破坏方法

枚举饿汉式能天然防止反射(反射调用时会对枚举类型进行相应的检查)、反序列化(反序列化时会对枚举类进行特殊的处理)破坏单例

2.2.1Unsafe 破坏单例
public enum Singleton2 {
    INSTANCE;

    // 以下代码,都是不必要的,为了测试方便而添加的

    // 构造方法,默认是 private 的,去掉以后,依然是私有的
    // idea也提示 Modifier 'private' is redundant for enum constructors 修饰符'private'对于enum构造函数是多余的
    // 主要是为了打印信息,看构造方法是否调用
    private Singleton2() {
        System.out.println("private Singleton2()");
    }

    // 打印枚举类时,把 hash 码也打印出来,能区分是否是同一对象,默认打印枚举的名字
    @Override
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    // 静态公共方法,获取单例
    public static Singleton2 getInstance() {
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}
    public static void main(String[] args) throws Exception {
        Singleton2.otherMethod();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        // 调用两次,看看是否是同一实例
        System.out.println(Singleton2.getInstance());
        System.out.println(Singleton2.getInstance());

        // Unsafe 破坏单例
        unsafe(Singleton2.class);
    }

    private static void unsafe(Class clazz) throws InstantiationException {
        Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);
        System.out.println("Unsafe 创建实例:" + o);
    }

控制台输出:
private Singleton2()
otherMethod()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
com.qq.qq.mal.rest.Singleton2@782830e
com.qq.qq.mal.rest.Singleton2@782830e
Unsafe 创建实例:com.hbis.ttie.mal.rest.Singleton2@5fdef03a

可以看到 Unsafe 创建了另一个对象

3.懒汉式单例 3.1介绍

在使用时才会创建单例对象

// 懒汉式单例
public class Singleton3 implements Serializable {
    private Singleton3() {
        System.out.println("private Singleton3()");
    }

    private static Singleton3 INSTANCE = null;
    
    public static Singleton3 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton3();
        }
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }

}
    public static void main(String[] args) throws Exception {
        Singleton3.otherMethod();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        System.out.println(Singleton3.getInstance());
        System.out.println(Singleton3.getInstance());

    }

控制台输出:
otherMethod()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
private Singleton3()
com.qq.qq.mal.rest.Singleton3@782830e
com.qq.qq.mal.rest.Singleton3@782830e

在调用 otherMethod() 时,未调用单例的构造方法,第一次调用 getInstance() 时,才会调用单例的构造方法,创建单例对象

3.2多线程环境运行

懒汉式单例需要考虑是否运行在多线程环境下,需要考虑线程安全的问题

两个线程,同时调用 getInstance() 方法,线程1通过 if (INSTANCE == null) 校验,进入代码块,并未执行 INSTANCE = new Singleton3() 时,线程2也通过了 if (INSTANCE == null) 的校验,进入代码块,线程1、线程2都执行INSTANCE = new Singleton3(),创建了连个对象,就不再是单例对象了

如何解决?将代码改进一下

// 懒汉式单例
public class Singleton3 implements Serializable {
    private Singleton3() {
        System.out.println("private Singleton3()");
    }

    private static Singleton3 INSTANCE = null;

    // Singleton3.class
    public static synchronized Singleton3 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton3();
        }
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

getInstance() 方法加 synchronized 关键字,对方法进行线程安全的保护

加在静态方法上的 synchronized,会给当前类(Singleton3.class)的 class 对象加一把锁,想进入方法,需要先获取锁,未释放锁前,其他线程无法进入

但是把 synchronized 加在整个方法上,虽然可以解决问题,但是性能上不好

查看代码发现,首次创建单例对象时,需要线程安全保护,单例对象创建后,就不存在线程安全问题了,因此,需求是首次创建单例对象时,有线程安全保护,后续的调用,无需线程安全保护

那么如何改进呢?

使用DCL懒汉式单例即双检锁懒汉式

4.DCL懒汉式单例 4.1介绍
// 懒汉式单例 - DCL
public class Singleton4 implements Serializable {
    private Singleton4() {
        System.out.println("private Singleton4()");
    }

    private static volatile Singleton4 INSTANCE = null; // 可见性,有序性

    public static Singleton4 getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton4.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton4();
                }
            }
        }
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}
    public static void main(String[] args) throws Exception {
        Singleton4.otherMethod();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        System.out.println(Singleton4.getInstance());
        System.out.println(Singleton4.getInstance());

    }

控制台输出:
otherMethod()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
private Singleton4()
com.qq.qq.mal.rest.Singleton4@782830e
com.qq.qq.mal.rest.Singleton4@782830e

代码的关键在于两次 if (INSTANCE == null) ,这也是双检索名字的来源

4.2为什么检查两次?

假如没有内部的检查

    public static Singleton4 getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton4.class) {
                INSTANCE = new Singleton4();
            }
        }
        return INSTANCE;
    }

首次创建单例对象时,线程1、线程2同时进行,因为 INSTANCE = null,所以可以通过外部          if (INSTANCE == null) 校验,进入 if 代码块,这次线程2首先拿到了锁,然后创建单例对象,然后解锁返回,这时线程1获得锁,进入代码块,因为没有内部 if (INSTANCE == null) 校验,会再次创建一个单例对象,返回解锁返回,这就有问题了;

4.3为什么必须使用 volatile ?

双检锁中,需要给静态变量使用 volatile 来修饰(volatile 可以解决共享变量的可见性,有序性问题),在双检锁中使用 volatile,是为了保证有序性

为何必须加 volatile?

`INSTANCE = new Singleton4()` 不是原子的,分成 3 步:创建对象、调用构造、给静态变量赋值

CPU可能会对指令的执行次序进行优化(如果两指令之间,没有因果关系,就可能会被调换执行次序)

创建对象即分配内存空间,一定会在调用构造、给静态变量赋值前执行,调用构造方法是目的是为了给当前实例的成员变量进行初始化赋值,静态变量赋值也是一个赋值操作

其中后两步可能被指令重排序优化,变成先赋值、再调用构造,在单线程下,调换顺序没有问题,是一种优化手段,但是如果多线程环境下,就可能有问题了

如果线程1 ,执行 INSTANCE = new Singleton4() ,先执行了赋值,还未调用构造,此时线程2 执行到第一个 if (INSTANCE == null) 校验,发现 INSTANCE 已经不为 null,就会返回,但是构造方法还没有执行,返回的是一个未完整构造的对象

解决方法就是给共享变量加 volatile 修饰(大意是加了 volatile 修饰,会在赋值语句后,加一个内存屏障,也就说是不会出现调用构造方法时的赋值,越过内存屏障,出现在给静态变量赋值之后)

4.4为什么饿汉式单例使用 volatile ?

饿汉式单例无需考虑多线程下对象创建的问题

    // 静态成员变量
    private static final Singleton1 INSTANCE = new Singleton1();

单例对象赋值给了静态成员变量,给静态变量赋值的操作,会放在这个类的静态代码块中执行,静态代码块中的线程安全有虚拟机来负责,我们无需考虑

5.懒汉式单例 - 内部类 5.1介绍

将对象的创建,放入静态代码块中,那就意味着是线程安全的

// 懒汉式单例 - 内部类
public class Singleton5 implements Serializable {
    private Singleton5() {
        System.out.println("private Singleton5()");
    }

    // 静态内部类
    private static class Holder {
        static Singleton5 INSTANCE = new Singleton5();
    }

    public static Singleton5 getInstance() {
        return Holder.INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

首先需要创建一个静态内部类,内部类可以访问外部类的私有变量,私有方法, 所以在内部类中创建了单例对象,并且赋值给了内部类的静态变量,刚才说了,静态变量的赋值是放在静态代码块中执行的,那么对象的创建(new Singleton5())就是线程安全的

在 getInstance() 方法中使用内部类访问它的变量,这是就会触发内部类的加载,链接,初始化,在初始化时,会创建单例对象,这样既有懒汉式的特性又能保证创建时的线程安全

    public static void main(String[] args) throws Exception {
        Singleton5.otherMethod();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        System.out.println(Singleton5.getInstance());
        System.out.println(Singleton5.getInstance());

    }

控制台输出:
otherMethod()
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
private Singleton5()
com.qq.qq.mal.rest.Singleton5@782830e
com.qq.qq.mal.rest.Singleton5@782830e
6.JDK 中哪些地方提现了单例模式?

* Runtime 体现了饿汉式单例


* Console 体现了双检锁懒汉式单例


* Collections 中的 EmptyNavigableSet 内部类懒汉式单例


* ReverseComparator.REVERSE_ORDER 内部类懒汉式单例
* Comparators.NaturalOrderComparator.INSTANCE 枚举饿汉式单例

 

 

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1039261.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号