effective解读-第三条 构建单例
单例:实例化一次的类,例如Spring容器通过IOC构建的Bean默认为单例模式
实现单例的方式
1. 静态域
优势:1. API简单清晰 2.简单(代码少,容易理解)
缺点:预防反射攻击和提供序列化需要更多代码的支持
public class SingletonInstance { public static final SingletonInstance singletonInstance=new SingletonInstance(); private SingletonInstance(){ } public void method(){ System.out.println(this); } public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //创建实例 SingletonInstance singletonInstance = SingletonInstance.singletonInstance; //反射创建实例 SingletonInstance.singletonInstance.method(); Constructor constructor = SingletonInstance.class.getDeclaredConstructor(); constructor.setAccessible(true); SingletonInstance o = (SingletonInstance) constructor.newInstance(); //结果为false,即该方式可能被AccessibleObject.setAccessible方式攻击 System.out.println(o==singletonInstance); } }
解决办法
/** * @author Programmer_Liu. * @since 2021/3/25 16:32 */ public class SingletonInstance { private static boolean flag = true; public static final SingletonInstance singletonInstance = new SingletonInstance(); private SingletonInstance() { if (flag){ flag = false; }else{ throw new RuntimeException("重复构建对象"); } } public void method() { System.out.println(this); } } class Main{ public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { SingletonInstance singletonInstance = SingletonInstance.singletonInstance; SingletonInstance.singletonInstance.method(); Constructor constructor = SingletonInstance.class.getDeclaredConstructor(); constructor.setAccessible(true); SingletonInstance o = (SingletonInstance) constructor.newInstance(); System.out.println(o == singletonInstance); } }
支持序列化
-
实现序列化接口
-
提供readResolve()方法
public class SingletonInstance implements Serializable { private static boolean flag = true; public static final SingletonInstance singletonInstance = new SingletonInstance(); private SingletonInstance() { if (flag){ flag = false; }else{ throw new RuntimeException("重复构建对象"); } } public void method() { System.out.println(this); } /** *保证反序列化单例的唯一 */ public Object readResolve(){ return singletonInstance; } } class Main{ public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException { SingletonInstance singletonInstance = SingletonInstance.singletonInstance; SingletonInstance.singletonInstance.method(); //Constructor constructor = SingletonInstance.class.getDeclaredConstructor(); //constructor.setAccessible(true); //SingletonInstance o = (SingletonInstance) constructor.newInstance(); //System.out.println(o == singletonInstance); //序列化 ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("C:\\Users\\管理员\\Desktop\\objectFile.obj")); out.writeObject(singletonInstance); //反序列化 ObjectInputStream in = new ObjectInputStream(new FileInputStream("C:\\Users\\管理员\\Desktop\\objectFile.obj")); SingletonInstance singletonInstance1 = (SingletonInstance)in.readObject(); //反序列化 ObjectInputStream in2 = new ObjectInputStream(new FileInputStream("C:\\Users\\管理员\\Desktop\\objectFile.obj")); SingletonInstance singletonInstance2 = (SingletonInstance)in2.readObject(); System.out.println(singletonInstance2==singletonInstance1); } }
2.静态工厂方式
优势:相比静态域的方式更灵活 1.不改变API的前提下可以改为每个线程返回唯一实例2.可以编写泛型类型3.使用者可以使用方法引用
至少需要上面的优势之一,否则应该有限考虑静态域的方式
缺点:比较复杂而且预防反射攻击和提供序列化需要更多代码的支持
//静态工厂方式的单例模式 public class SingletonInstance { private static boolean flag = true; private static final SingletonInstance singletonInstance = new SingletonInstance(); private SingletonInstance() { if (flag){ flag = false; }else{ throw new RuntimeException("重复构建对象"); } } public static SingletonInstance getInstance(){ return singletonInstance; } public void method() { System.out.println(this); } } class Main{ public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { SingletonInstance singletonInstance = SingletonInstance.getInstance(); singletonInstance.method(); } }
1.线程单例
public class SingletonInstance { private static ThreadLocal<Boolean> flag = ThreadLocal.withInitial(() -> Boolean.TRUE); private static final ThreadLocal<SingletonInstance> LOCAL = ThreadLocal.withInitial(SingletonInstance::new); private SingletonInstance() { if (flag.get()) { flag.set(false); } else { throw new RuntimeException("重复构建对象"); } } public static SingletonInstance getInstance() { SingletonInstance singletonInstance = LOCAL.get(); return singletonInstance; } public void method() { System.out.println(this); } } class Main { public static void main(String[] args) throws InterruptedException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { SingletonInstance singletonInstance = SingletonInstance.getInstance(); singletonInstance.method(); //Constructor constructor = SingletonInstance.class.getDeclaredConstructor(); //constructor.setAccessible(true); //SingletonInstance o = (SingletonInstance) constructor.newInstance(); new Thread(() -> { SingletonInstance singletonInstance2 = SingletonInstance.getInstance(); singletonInstance2.method(); }).start(); Thread.sleep(2000); SingletonInstance singletonInstance3 = SingletonInstance.getInstance(); singletonInstance3.method(); } }
2.泛型单例工厂
//接口 public interface UnaryFunction<T> { T apply(T args); } //工厂 public class UnaryFunctionFactory { private static UnaryFunction<T> unaryFunction = args -> args; /** * 需要返回什么类型就转为什么类型 * 使用层面:用什么类型接收就转为什么类型 */ public static<T> UnaryFunction<T> getInstance(){ return (UnaryFunction<T>)unaryFunction; } public static void main(String[] args) { //用Integer类型接收 UnaryFunction<Integer> s1=UnaryFunctionFactory.getInstance(); Integer apply = s1.apply(111); //用String类型接收 UnaryFunction<String> s2=UnaryFunctionFactory.getInstance(); String hello = s2.apply("hello"); } }
3.方法引用
public class SingletonInstance{ private static boolean flag = true; private static final SingletonInstance singletonInstance = new SingletonInstance(); private SingletonInstance() { if (flag) { flag = false; } else { throw new RuntimeException("重复构建对象"); } } public static SingletonInstance getInstance() { return singletonInstance; } public void method() { System.out.println(this); } } class Main { //提供者接口中可以直接通过方法引用调用 public SingletonInstance s(Supplier<SingletonInstance> s){ return s.get(); } public static void main(String[] args){ Main main = new Main(); SingletonInstance s = main.s(SingletonInstance::getInstance); s.method(); } }
3. 单元素枚举
优势:更加简单,内部提供了序列化机制并预防反射攻击。单元素枚举是实现Singleton的最佳方案。
缺点:无法继承超类,因为枚举类型默认继承了Enum类
作者:刘志红
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!