下面代码是一个简易的单例模式,在类加载的时候就会调用私有构造方法创建一个INSTANCE。此时只要运行main函数就会加载Elvis类,即使main函数中一行代码也没有,控制台也会输出一句“调用了私有构造方法”。无论多用多少次 Elvis.INSTANCE 返回的都是同一个对象。

public class Elvis {
    // 方式一:
    public static final Elvis INSTANCE = new Elvis();

    private Elvis(){
        System.out.println("创建了一个Elvis对象");
    }

}

但是我们依然可以利用反射技术来创建出多个 Elvis 对象:

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    Elvis elvis1 = Elvis.INSTANCE;
    Elvis elvis2 = Elvis.INSTANCE;
    System.out.println(elvis1==elvis2);

    Constructor<Elvis> constructor = Elvis.class.getDeclaredConstructor();
    constructor.setAccessible(false); // 设置私有构造器的可访问性
    Elvis elvis3 = constructor.newInstance(); // 调用私有构造器创建对象
    System.out.println(elvis1==elvis3);

}

可以在被要求创建第二个实例的时候抛出异常进行防御:

private Elvis(){
    if(INSTANCE !=null ){
        try {
            throw new Exception("防御");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    System.out.println("调用了私有构造方法");
}

使用静态工厂方法的Singleton:

public class Elvis {
    // 方式二:
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() {}
    public static Elvis getInstance() {return INSTANCE;}

}

这种方式的优点书上写了,但是我看不懂,但是这种方法依然存在通过反射创建多个实例的问题。

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {

        // 通过反序列化创建多个Singleton实例

        Elvis elvis1 = Elvis.getInstance();

        // 创建 ByteArrayOutputStream 对象来序列化对象到字节数组
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(baos);
        // 将对象写入输出流
        out.writeObject(elvis1);
        // 关闭输出流
        out.close();
        byte[] serializedData = baos.toByteArray();

        // 创建 ByteArrayInputStream 对象来反序列化字节数组为对象
        ByteArrayInputStream bais = new ByteArrayInputStream(serializedData);
        ObjectInputStream in = new ObjectInputStream(bais);
        // 从输入流中读取对象
        Elvis elvis2 = (Elvis) in.readObject();
        // 关闭输入流
        in.close();

        System.out.println(elvis1==elvis2); 
        // 输出false,说明通过反序列化创建出来的实例和之前的实例不同
        
    }

 第三种创建单例的方式采用枚举的方法来实现,也是我们的首选方法:

package com.example.redisDemo.effective_java;

import java.io.*;
import java.lang.reflect.InvocationTargetException;

public enum MyEnum  {
    INSTANCE;
    public void leaveTheBuilding() {}

    public static void main1(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        MyEnum myEnum1 = MyEnum.INSTANCE;
        MyEnum myEnum2 = MyEnum.INSTANCE;
        System.out.println(myEnum2==myEnum1);

        // 创建 ByteArrayOutputStream 对象来序列化对象到字节数组
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(baos);
        // 将对象写入输出流
        out.writeObject(myEnum1);
        // 关闭输出流
        out.close();
        byte[] serializedData = baos.toByteArray();

        // 创建 ByteArrayInputStream 对象来反序列化字节数组为对象
        ByteArrayInputStream bais = new ByteArrayInputStream(serializedData);
        ObjectInputStream in = new ObjectInputStream(bais);
        // 从输入流中读取对象
        MyEnum myEnum3 = (MyEnum) in.readObject();
        // 关闭输入流
        in.close();
        System.out.println(myEnum1==myEnum3);
    }

    public static void main(String[] args) throws Exception {
        MyEnum instance1 = MyEnum.INSTANCE;

        // 获取枚举类型 MyEnum 的 Class 对象
        Class<?> enumClass = MyEnum.class;

        // 使用反射获取 INSTANCE 实例
        Object instance2 = Enum.valueOf((Class<MyEnum>) enumClass, "INSTANCE");

        // 判断是否为 MyEnum 类型的枚举实例
        if (enumClass.isInstance(instance2)) {
            MyEnum myInstance = (MyEnum) instance2;
            System.out.println(myInstance==instance1);
        }
    }
}

上述代码中main1函数两次输出结果均为true,main函数使用反射手段得到的instance也和MyEnum.INSTANCE用一个实例说明三者为同一个实例。

04-04 03:26