单例模式
单例模式注意事项和细节说明
- 什么是单例模式?
- 将构造函数私有化
- 单例模式保证了,系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统的性能。
- 当想实例化一个单例类的时候,必须要使用相应的获取对象的方法,而不是使用new
- 单例模式使用场景:
- 需要频繁的创建和销毁的对象,创建对象耗时过多或耗费资源过度(即:重量级对象),但又需要经常用到的对象,比如工具类,平凡访问数据库或文件的对象(比如数据源,session工厂等)
- 在JDK中Runtime就是经典的单例模式,使用的是饿汉式
- 饿汉式:
- 内部加载对象,如果对该类没有使用就会造成内存浪费
- 懒汉式
- 一般常用的是枚举的方式和静态内部类的方式创建单例类
- 静态内部类:在实例化调用方法的时候的时候才会加载静态内部类,并且只会加载一次,让JVM帮我们保证了线程的安全性。
- 枚举:能够避免多线程问题,并且还能防止反序列化和反射重新创建新的对象
- 一般常用的是枚举的方式和静态内部类的方式创建单例类
饿汉式(静态常量)
- 优点:在类装载的时候就完成实例化。避免了线程同步问题
- 缺点:在类装载的时候完成实例化,如果从来未使用过这个实例,就会造成内存的浪费
饿汉式(静态代码块)
- 优点/缺点:和静态常量一样
懒汉式(线程不安全问题)
- 优缺点:
- 只能在单线程模式下使用。
- 如果多线程下,一个线程进入了if判断语句,还未来得及向下执行,另外一个线程也通过了if判断语句,就会产生多个实例
- 结论:在实际开发中不建议使用这种方式
懒汉式(线程安全,同步方法)
- 优点:解决了线程不安全问题
- 缺点,效率而太低
- 结论:实际开发中不推荐
懒汉式(面试陷阱)
- 加载if判断里面,锁住创建对象语句,不做任何其它处理,很垃圾,连线程安全都解决不了
双重检查
- 优缺点:
- 进行两次if判断保证线程安全,
- 同时可以使用多线程保证开发效率
静态内部类的方式
- 这种方式采用了类加载的机制来保证初始化实例时只有一个线程。
- 静态内部类方式在Singleton类加载的时候并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装在SingletonInstance类,从而完成Singleton的实例化
- 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程安全,在类进行初始化时,别的线程是无法进入的。
- 优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
- 结论:推荐使用
枚举
- 借助JDK1.5中添加的枚举来实现单例模式。不仅能够防止线程同步问题,而且还能防止反序列化重新创建新的对象
工厂模式
什么是工厂模式:
- 这种设计模式是一种创建模式,是一种很好的创建对象的方式,他不会对客户端暴露创建对象的逻辑,而是通过公共的接口指向一个新创建的对象。
简单工厂模式:
- 符合实际业务,平时用得比较多。
- 通过一个工厂类去实现管理多个类来生成对象,但是这样的话每次新增类都需要改变工厂类的代码,违背了开闭原则
工厂方法模式:
- 符合设计模式原则,但并不太适合实际业务开发。
- 通过一个工厂接口,来规范各个类的工厂类,这样不会修改原来的代码,
- 只需要重现创建对应这个类的工厂,使用的时候去调用这个类的工厂就可以了,
- 很符合设计模式原则,但是如果更改和开发的时候会非常麻烦,同时导致代码复杂性大大提高,并不太推荐平时业务中使用。
抽象工厂模式
-
定义:抽象工厂提供了一个创建一系列相关或者互相依赖对象的接口,无需指定他们具体的类
-
使用场景:
- 客户端(应用层)不依赖于产品类实例如何被创建,实现等细节
- 强调一系列相关产品对象,一起使用创建对象需要大量的重复代码
-
优点:
- 具体产品在应用层的代码隔离,无需关心创建的细节
- 将一个系列的产品统一到一起创建
-
缺点:
- 规定了所有可能被创建的产品集合,铲平簇中拓展新的产品困难
- 增加了系统的抽象性和理解难度
代理模式
基本介绍:
- 代理模式:当需要对某一个类进行修改的时候,为了防止影响到其它使用它的模块,同时为了符合开闭原则,我们可以为这个类提供一个代理对象,这个代理对象可以实现原有对象的功能,同时对功能进行增强,即拓展目标对象的功能功能
- 被代理的对象可以是
- 远程对象,
- 创建开销大的对象(比如Spring容器种某些对象创建开销非常大,所以我们将他放到容器中同时是单例模式,这个时候需要对这个对象进行增强,但是重新创建一个对象开销太大了,这个时候就可以从容器中拿出这个对象来对他进行增强,这就涉及到了Spring的AOP)
- 需要安全控制的对象
- 代理模式有不同的形式,主要是三种:
- 静态代理模式
- 动态代理模式(JDK代理模式,接口代理模式)
- Cglib代理(可以在内存动态的创建对象,而不需要实现接口,他是属于动态代理的范畴)
静态代理
- 静态代理模式的基本介绍:
- 静态代理在使用是,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者继承相同的父类
- 优点:在不修改目标对象代码的情况下,能够通过代理对象对目标功能拓展
- 缺点:因为每次代理对象的创建都需要与目标对象实现一样的接口,所以会产生很多的代理类,而且一旦接口增加了方法,目标对象和代理对象都需要进行维护。
动态代理:
- 动态代理基本介绍:
- 代理对象不需要实现接口,但是目标对象需要实现接口,否则不能使用动态代理。
- 代理对象生成是利用JDK的API,通过反射的方式动态的在内存中构建代理对象
- 动态代理也叫JDK代理,接口代理
- JDK中生成代理对象的APi
- 代理类所在的包是:lang proxy
- 实现Jdk的动态代理需要newProxyInstance,然后通过重写InvocationHandler来实现动态代理
Cglib代理
- Cglib代理介绍:
- 往往使用静态代理和JDk代理都需要目标对象实现接口,如果没有实现接口就无法完成,但是Cglib代理就可以代理没有实现接口的目标对象。
- Cglib代理也叫做子类代理,他是在内存中构建一个子类对象从而实现为目标兑现完成功能拓展,也归属于动态代理。
- 他被广泛的应用于许多AOP框架,比如Spring AOP,实现方法的拦截
- Cglib代理实现步骤
- 在内存中构建子类,但是代理对象不能为final,
- 同时目标方法如果为final/static,那么也不会被拦截,即不会执行目标对象的额外业务方法。
SpringAOP模块
- 常用的注解有
- @Pointcut
- @Arund环绕通知
- @Before前置通知
- @After后置通知
- @AfterThrowing 异常通知
- @AfterReturning结果通知
- 正常执行情况
- 1.先是Around的环绕之前
- 2.然后是@Before的前置插入
- 3.方法 return返回值
- 3.然后是@AfterReturning的结果通知
- 4.然后是@After后置插入 就算出现异常也会执行
- 5.然后是@Around的环绕之后
- 如果出现异常
- 1.先是Around的环绕之前
- 2.然后是@Before的前置插入
- 3.方法
- 4.@AfterThrowing 异常通知
- 5.然后是@After后置插入 就算出现异常也会执行