单例模式 我不是女神ヾ 2024-04-19 07:20 0阅读 0赞 # 单例模式 # 定义:保证一个类仅有一个实例,并提供一个全局访问点 类型:创建型 ## 适用场景 ## 想确保任何情况下都绝对只有一个实例 ## 优点 ## * 在内存里只有一个实例,减少了内存开销 * 可以避免对资源的多重占用 * 设置全局访问点,严格控制访问 ## 缺点 ## * 没有接口,扩展困难 ## 重点 ## * 私有构造器 * 线程安全 * 延迟加载 * 序列化和反序列化安全 * 反射 ## 懒汉式 ## 使用的时候才初始化 public class LazySingleton { // 初始化不创建 private static LazySingleton lazySingleton = null; // 私有构造器 private LazySingleton() { } // 获取lazysingleton对象的方法 public synchronized static LazySingleton getInstance(){ if(lazySingleton == null){ lazySingleton = new LazySingleton(); } return lazySingleton; } } 在多线程下会有问题,在run模式下打印出来结果是同一个对象是因为结果被最后一个线程覆盖了,是最新的对象,在debug模式下就会看到两个实例对象了。 > progrem end > Thread-0singleton.LazySingleton@42337127 > Thread-1singleton.LazySingleton@4233712 改进 使用synchronized同步锁 public synchronized static LazySingleton getInstance(){ if(lazySingleton == null){ lazySingleton = new LazySingleton(); } return lazySingleton; } // synchronized类代码块,与上面的写法效果一致 public static LazySingleton getInstance(){ synchronized (LazySingleton.class){ if(lazySingleton == null){ lazySingleton = new LazySingleton(); } } return lazySingleton; } 在方法上加synchronized同步锁或是用同步代码块对类加同步锁,此种方式虽然解决了多个实例对象问题,但是该方式运行效率却很低下,下一个线程想要获取对象,就必须等待上一个线程释放锁之后,才可以继续运行。 继续改进,双重检查 public static LazyDoubleCheckSingleton getInstance(){ if(lazyDoubleCheckSingleton == null){ synchronized (LazyDoubleCheckSingleton.class){ if(lazyDoubleCheckSingleton == null){ lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton(); } } } return lazyDoubleCheckSingleton; } 使用双重检查进一步做了优化,可以避免整个方法被锁,只对需要锁的代码部分加锁,可以提高执行效率。 ## 静态内部类 ## 懒加载 public class StaticInnerClassSingleton { private static class InnerClass{ private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton(); } public static StaticInnerClassSingleton getInstance(){ return InnerClass.staticInnerClassSingleton; } private StaticInnerClassSingleton() { } } ![format_png][] 只有一个线程去获得InnerClass这个静态内部类的初始化锁去初始化 只有一个线程对内部类进行类加载 ## 饿汉式 ## 在类加载的时候就创建,只会有一个线程进行类加载 public class HungrySingleton{ private final static HungrySingleton hungrySingleton=new HungrySingleton(); private HungrySingleton(){ } public static HungrySingleton getInstance(){ return hungrySingleton; } } 声明为final必须在类加载的时候就初始化好,被定义为static字段或在静态代码块中初始化 public class HungrySingleton{ private final static HungrySingleton hungrySingleton; static{ hungrySingleton = new HungrySingleton(); } private HungrySingleton(){ } public static HungrySingleton getInstance(){ return hungrySingleton; } } ## 序列化破坏单例模式 ## 将对象序列化到文件中,然后再从文件中取出来,这两个对象还是一个对象吗? 先让对象实现Serializable接口 public class HungrySingleton implements Serializable{ private final static HungrySingleton hungrySingleton; static{ hungrySingleton = new HungrySingleton(); } private HungrySingleton(){ } public static HungrySingleton getInstance(){ return hungrySingleton; } } public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { HungrySingleton instance = HungrySingleton.getInstance(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file")); oos.writeObject(instance); File file = new File("singleton_file"); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); HungrySingleton newInstance = (HungrySingleton) ois.readObject(); System.out.println(instance); System.out.println(newInstance); System.out.println(instance == newInstance); } } > com.geely.design.pattern.creational.singleton.HungrySingleton@1973e9b com.geely.design.pattern.creational.singleton.HungrySingleton@1663380 > false 发现序列化,然后反序列化得到的不是同一个对象为了解决这个问题,需要再添加一个方法 public class HungrySingleton implements Serializable{ private final static HungrySingleton hungrySingleton; static{ hungrySingleton = new HungrySingleton(); } private HungrySingleton(){ } public static HungrySingleton getInstance(){ return hungrySingleton; } private Object readResolve(){ return hungrySingleton; } } > com.geely.design.pattern.creational.singleton.HungrySingleton@1973e9b com.geely.design.pattern.creational.singleton.HungrySingleton@1973e9b > true 添加了一个readResolve方法,发现问题解决了。 ## 解决反射攻击 ## 构造器是私有的,但可以通过反射把构造器的权限打开 public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class objectClass = HungrySingleton.class; Constructor constructor = objectClass.getDeclaredConstructor(); constructor.setAccessible(true); HungrySingleton newInstance = (HungrySingleton) constructor.newInstance(); HungrySingleton instance = HungrySingleton.getInstance(); System.out.println(instance); System.out.println(newInstance); System.out.println(instance == newInstance); } } > com.geely.design.pattern.creational.singleton.HungrySingleton@8cf4c6 > com.geely.design.pattern.creational.singleton.HungrySingleton@edcd21 > false 在类加载时创建对象,可以这样修改其构造方法添加异常 饿汉式和内部静态类的单例都可以这样修改 public class HungrySingleton implements Serializable{ private final static HungrySingleton hungrySingleton; static{ hungrySingleton = new HungrySingleton(); } private HungrySingleton(){ if(hungrySingleton != null){ throw new RuntimeException("单例构造器禁止反射调用"); } } public static HungrySingleton getInstance(){ return hungrySingleton; } } 对于懒汉式这种不是在类加载时创建对象的,无法避免反射攻击 因为懒汉式避免多线程的时候是在方法中避免的,但用反射的时候根本不用通过这个方法创建,想啥时候创建啥时候创建。但在类加载的时候有class锁避免多线程,所以可以避免反射攻击。 ## 枚举 ## 不受序列化影响 不能被反射 public enum EnumInstance { INSTANCE; private Object data; public Object getData() { return data; } public void setData(Object data) { this.data = data; } public static EnumInstance getInstance(){ return INSTANCE; } } [format_png]: https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4ubmxhcmsuY29tL3l1cXVlLzAvMjAxOS9wbmcvMTEyNzcyLzE1NTUwOTMzMDI3NTYtYjRjNTUyZTAtOWJiZC00M2NjLWIwOGEtZGQ2YTg5ZWE1MzNjLnBuZw?x-oss-process=image/format,png
相关 单例模式 http://blog.csdn.net/zhengzhb/article/details/7331369 定义:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实 曾经终败给现在/ 2022年09月25日 15:30/ 0 赞/ 185 阅读
相关 单例模式 class sigle{ protected static $ins = null; public function getIns(){ 深藏阁楼爱情的钟/ 2022年07月20日 20:27/ 0 赞/ 186 阅读
相关 单例模式 单例模式是一种常用的软件设计模式。 单例模式:保证一个类只有一个实例且实例易于外界访问; 单例模式的三个要点: 1.某个类只能有一个实例; 2.它必须自行创建 叁歲伎倆/ 2022年06月07日 06:13/ 0 赞/ 85 阅读
相关 单例模式 <table> <tbody> <tr> <td style="vertical-align:top;width:.6868in;"> <p style 矫情吗;*/ 2021年11月22日 10:52/ 0 赞/ 226 阅读
相关 单例模式 单例模式 单例模式(SingletonPattern)是java中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及 红太狼/ 2021年11月16日 05:30/ 0 赞/ 244 阅读
相关 单例模式 应用场景 1. windows的任务管理器 2. 网站的计数器 3. 应用程序的日志 4. 数据库连接池,因为数据库连接是一种数据库资源。数据库软件系统中使用数据 r囧r小猫/ 2021年11月11日 15:08/ 0 赞/ 290 阅读
相关 单例模式 单例模式有以下特征: 1. 只有一个对象存在 2. 对象的实例化必须在类中实现 一、懒汉模式(线程不安全) package com.kevin; 谁借莪1个温暖的怀抱¢/ 2021年10月01日 07:48/ 0 赞/ 246 阅读
相关 单例模式 1.定义 单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。 2.实现步骤 1. 将该类的构造方法定义为私有方法,这样其他处 小咪咪/ 2021年09月27日 13:56/ 0 赞/ 323 阅读
相关 单例模式 单例模式Singleton: 意图: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 动机: 对于一些类来说,只有一个实例是很重要的。虽然系统中可以由很多打印机 忘是亡心i/ 2021年09月26日 13:58/ 0 赞/ 252 阅读
相关 单例模式 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责 Dear 丶/ 2021年09月17日 02:10/ 0 赞/ 264 阅读
还没有评论,来说两句吧...