• java中常见的设计模式


    最早概念是在建筑领域产生的,后来被引入到软件开发领域。

    模式是解决一类问题的固定写法,一个模式用来解决一种问题,经过反复优化,最终得出来的。之前的程序员们,在工作中对某一类问题解决方式进行总结归纳,一种问题对应着一个模式。设计模式就是在面向对象的基础上,更加合理的组织类与类之间的关系。

    为什么学习设计模式?

    学习别人的经验,拿来为我们所用,提高编程设计能力,让程序设计更加标准合理,提高开发效率。提高程序的维护性,能够看懂复杂的源码。

    1、单例模式

    解决的问题:在一个程序中,一个类只能创建一个对象。 举例:windows任务管理器,只能打开一个。

    实现:把构造方法私有化,在其他类中不能访问了,不能创建对象。可以在单例类中,向外界提供一个方法,访问到唯一的单例对象。

    1.饿汉式单例(急切式)

    在类加载时就创建唯一的对象,不存在线程安全问题

    1. public class Window {
    2. //饿汉式单例, 静态的成员在类被加载时,就将唯一的window对象创建
    3. //没有任何的线程安全问题
    4. private static Window window = new Window();
    5. private Window(){}
    6. //调用getWindow,返回对象即可
    7. public static Window getWindow(){
    8. return window;
    9. }
    10. }
    1. public class Test {
    2. public static void main(String[] args) {
    3. for (int i = 0; i < 10; i++) {
    4. new Thread(()->{
    5. System.out.println(Window.getWindow());
    6. }).start();
    7. }
    8. }
    9. }

    Runtime类属于典型的单例设计,从源码中可以看出是饿汉式单例。利用Runtime类可以启动新的进程或进行相关运行时环境的操作。比如,取得内存空间以及释放垃圾空间。

    Runtime.getRuntime();

    2.懒汉式

    在类加载的时候不创建对象,在使用时创建,生成对象的数量就需要我们自己来进行控制。

    但是会出现线程安全问题:在多线程访问时,可能会有多个线程同时进行到if,就会创建出多个对象。

    线程安全问题的解决:

    1、给方法加锁,虽然可以解决,但是效率低,一次只能有一个线程进入获取。

    1. public class Window {
    2. //在类加载的时候不创建改类的对象,在第一次获取单例对象时创建
    3. private static Window window=null;
    4. private Window(){}
    5. public static synchronized Window getWindow(){
    6. if(window==null){
    7. window = new Window();
    8. }
    9. return window;
    10. }
    11. }
    1. public class Test {
    2. public static void main(String[] args) {
    3. for (int i = 0; i < 10; i++) {
    4. new Thread(()->{
    5. System.out.println(Window.getWindow());
    6. }).start();
    7. }
    8. }
    9. }

    2、给代码块加锁,双重检索+volatile

    window = new Window();这一句代码可以分为3条指令执行,万一指令重排序,会导致出现一个半成品对象返回。

    为什么双重检索?先确保有一个线程把对象new出来,后面的线程来了之后,只要走第一个判断就好了。一旦对象创建之后,之后效率就变高了,不用进入同步代码块里面了。

    1. public class Window {
    2. //禁止整个过程中指令重排
    3. private static volatile Window window=null;
    4. private Window(){}
    5. public static Window getWindow(){
    6. if(window==null){//如果没有的话,相当于给方法加锁
    7. synchronized(Window.class){
    8. if(window == null){//如果没有的话,相当于没有加锁
    9. window = new Window();
    10. }//一旦对象创建之后,之后效率就变高了,不用进入同步代码块里面了
    11. }
    12. }
    13. return window;
    14. }
    15. }

    2、工厂模式

    工厂--->生产对象--->spring(把创建对象的权利反转给spring框架,把创建对象和使用对象分离

    有一个工厂专门负责创建对象,使用对象时只需要找对应的工厂。根据我们产生对象的场景,需求的不同,又分为:简单工厂模式、工厂方法模式、抽象工厂模式。

    1.简单工厂模式

    工厂只有一个。一个工厂类负责同一类所有产品的创建;但是扩展不方便,如果增加或减少产品时,需要修改工厂类,违背了开闭原则。

    该模式中包含的角色:

    工厂:简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类提供静态方法,可以被外界直接调用,创建所需的产品对象。

    抽象产品:所有对象的父类,描述所有实例共有的接口。可以是抽象类或接口。

    具体产品:是简单工厂模式的创建目标

    优点:客户端不负责对象的创建,而是由专门的工厂类完成。客户端只负责对象的调用,实现了创建和调用的分离

    缺点:扩展不方便,如果增加和减少子类,要修改简单工厂类,违反了开闭原则

    1. public interface Car {
    2. void run();
    3. }
    1. public class Aodi implements Car{
    2. @Override
    3. public void run() {
    4. System.out.println("奥迪汽车行驶");
    5. }
    6. }
    1. public class Bmw implements Car{
    2. @Override
    3. public void run() {
    4. System.out.println("宝马汽车行驶");
    5. }
    6. }
    1. public class CarFactory {
    2. public static Car createCar(String name){
    3. if(name.equals("aodi")){
    4. Aodi aodi = new Aodi();
    5. return aodi;
    6. }
    7. if(name.equals("bmw")){
    8. return new Bmw();
    9. }
    10. return null;
    11. }
    12. }
    1. public class Test {
    2. public static void main(String[] args) {
    3. Car bmw = CarFactory.createCar("bmw");
    4. Car aodi = CarFactory.createCar("aodi");
    5. bmw.run();
    6. aodi.run();
    7. }
    8. }
    2.工厂方法模式

    添加一个抽象工厂,具体工厂实现抽象工厂。一个具体工厂负责创建一个具体的产品,即使这些产品是有联系的,也必须由不同的工厂分别创建。如果产品增加或减少,只增加产品子类和工厂子类,符合开闭原则。

    • 抽象工厂:工厂方法模式的核心,任何在模式中创建的对象的工厂类必须实现这个接口。
    • 具体工厂:实现抽象工厂接口的具体工厂类。
    • 抽象产品:产品对象的共同父类或接口。
    • 具体产品:实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往——对应。

    好处:符合开闭原则。若增加和减少产品子类,只增加产品子类和工厂子类,不需要修改其他产品。

    缺点:需要额外的编写代码,增加了工作量。

    适用场景:所有的产品子类是同一类的、比较多的。

    1. public interface Car {
    2. void run();
    3. }
    1. public class Aodi implements Car {
    2. @Override
    3. public void run() {
    4. System.out.println("奥迪汽车行驶");
    5. }
    6. }
    1. public class Bmw implements Car {
    2. @Override
    3. public void run() {
    4. System.out.println("宝马汽车行驶");
    5. }
    6. }
    1. public interface CarFactory {
    2. Car createCar();
    3. }
    1. public class AodiFactory implements CarFactory{
    2. @Override
    3. public Car createCar() {
    4. return new Aodi();
    5. }
    6. }
    1. public class BmwFactory implements CarFactory{
    2. @Override
    3. public Car createCar() {
    4. return new Bmw();
    5. }
    6. }
    3.抽象工厂模式

    一个工厂创建一个系列的产品,可以造出这个厂家所有的产品。例如华为工厂可以创建汽车产品、手机产品

    • 抽象工厂:同一品牌中所有系列的产品
    • 具体工厂:负责创建同一个品牌的产品。
    • 抽象产品
    • 具体产品

    1. public interface AbstractFactory {
    2. Car getCar();
    3. Phone getPhone();
    4. }
    1. public class AodiFactory implements AbstractFactory{
    2. @Override
    3. public Car getCar() {
    4. return new AodiCar();
    5. }
    6. @Override
    7. public Phone getPhone() {
    8. return new AodiPhone();
    9. }
    10. }
    1. public class BmwFactory implements AbstractFactory{
    2. @Override
    3. public Car getCar() {
    4. return new BmwCar();
    5. }
    6. @Override
    7. public Phone getPhone() {
    8. return new BmwPhone();
    9. }
    10. }
    1. public interface Car {
    2. void run();
    3. }
    1. public interface Phone {
    2. void call();
    3. }
    1. public class AodiCar implements Car{
    2. @Override
    3. public void run() {
    4. System.out.println("奥迪汽车行驶");
    5. }
    6. }
    1. public class AodiPhone implements Phone{
    2. @Override
    3. public void call() {
    4. System.out.println("奥迪手机打电话");
    5. }
    6. }
    1. public class Test {
    2. public static void main(String[] args) {
    3. AbstractFactory aodiFactory = new AodiFactory();
    4. Car aodiCar = aodiFactory.getCar();
    5. Phone aodiphone = aodiFactory.getPhone();
    6. aodiCar.run();
    7. aodiphone.call();
    8. AbstractFactory bmwFactory = new BmwFactory();
    9. Car bmwCar = bmwFactory.getCar();
    10. Phone bmwPhone = bmwFactory.getPhone();
    11. bmwCar.run();
    12. bmwPhone.call();
    13. }
    14. }

    3、原型模式

    spring创建bean作用域:

    • singleton:单例bean,一个类创建一个对象,spring启动时就创建单例对象。
    • prototype: 原型的(多例),每次获取时,都会创建一个新的对象。

    创建一些内容相同的对象,如果每次去new就比较耗时间。可以造出来一个,然后对对象进行克隆,提高创建的效率。(注意深克隆和浅克隆)

    4、代理模式

    最早在spring框架中的aop提到。面向切面编程:可以对业务逻辑和非业务逻辑进行隔离,将程序中的一些非业务代码进行提取,然后让代理对象去调用公共的方法,就可以做到在不需要修改原来代码的情况下,为程序添加额外的功能。

    底层实现:使用的是动态代理模式。是通过一个代理对象来实现对非业务代码进行调用的。告诉代理对象,调用哪个方法时,让代理对象去帮助我们调用哪个方法。

    代理模式:分为静态代理和动态代理。

    1.静态代理

    目标对象(汽车厂卖汽车,最终要具体干这件事的主体)不能/不方便操作某些事情,这时可以为目标对象提供一个代理对象,代理对象代表目标对象。在调用目标对象方法时,可以为目标对象添加一些额外功能。

    角色:

    • 抽象主体(接口)
    • 真实主体(目标对象):实际做事情
    • 代理对象:代表目标对象

    静态代理实现:目标对象和代理对象必须实现相同的接口。如果需要代理新的目标,需要对代理对象进行修改,必须添加实现新的接口,这样违背开闭原则,扩展起来不方便一般框架中不使用,自己在一些简单的功能中可以使用。

    public class StaticProxy implements SellCar,SellPhone{}

    1. //抽象操作定义 卖东西
    2. public interface Sell {
    3. void sell();
    4. }
    1. //汽车厂
    2. public class CarFactoryImpl implements Sell {
    3. @Override
    4. public void sell() {
    5. System.out.println("汽车厂卖汽车");
    6. }
    7. }
    1. //静态代理,实际中很少使用静态代理,因为其代理类实现的接口必须与目标类实现接口一致,扩展起来就比较麻烦
    2. public class StaticProxy implements Sell {
    3. Sell sell;
    4. public StaticProxy(Sell sell) {
    5. this.sell = sell;
    6. }
    7. //代理对象中的方法
    8. @Override
    9. public void sell() {
    10. System.out.println("汽车介绍");
    11. sell.sell();//最终执行者还是目标对象
    12. System.out.println("办理手续");
    13. }
    14. }
    1. public class Test {
    2. public static void main(String[] args) {
    3. Sell carFactory = new CarFactoryImpl();
    4. //创建汽车厂代理对象
    5. StaticProxy staticProxy = new StaticProxy(carFactory);
    6. staticProxy.sell();//看似访问的是代理类,其实是目标对象来做的
    7. }
    8. }
    9. /*
    10. 汽车介绍
    11. 汽车厂卖汽车
    12. 办理手续
    13. */
    2.动态代理

    为目标类动态的生成代理对象,这样就可以代理任意的目标类。动态代理分为:jdk代理和cglib代理。

    在spring框架中两种代理方式都支持,如果一个类实现了接口,就采用jdk代理;如果一个类没有实现接口,就采用的cglib代理。

    (1)jdk代理

    底层使用的反射机制,动态获取目标类对象所实现的接口,然后为目标类动态生成一个代理对象,并动态获取到要调用的方法,最终调用目标对象中的方法

    动态代理可以代理任意的目标类,但是目标类必须要实现一个接口

    动态代理器中,可以传进来任意的目标类。在getProxy方法中,获取到目标对象的接口、拿到目标对象的信息,最终为目标对象生成代理对象。代理对象实现了目标对象的接口,最后代理对象调用目标类的接口中的sell方法,其实会调用代理器类中的invoke方法。然后通过反射机制,调用目标对象中的方法。

    1. //抽象操作定义 卖东西
    2. public interface Sell {
    3. void sell();
    4. }
    1. //汽车厂
    2. public class CarFactoryImpl implements Sell {
    3. @Override
    4. public void sell() {
    5. System.out.println("汽车厂卖汽车");
    6. }
    7. }
    1. /*
    2. 动态代理类
    3. 代理类不需要实现与目标类相同的接口,这样就可以代理任意的目标类
    4. 但是是有要求的,目标类必需实现接口,此种方式是动态代理的实现方式之一: jdk代理 是一种纯反射机制实现(动态获取目标类接口方法)
    5. */
    6. public class DynamicProxy implements InvocationHandler {
    7. Object object;//真实对象,接收任何的目标类对象
    8. public DynamicProxy(Object object) {
    9. this.object = object;
    10. }
    11. /*
    12. 在代理类中调用目标类中的具体方法,
    13. 动态的将代理动态对象,目标类中要调用的方法,及方法中的参数传递过来
    14. Method method 动态获取的真正要执行的方法
    15. */
    16. @Override
    17. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    18. System.out.println("之前开启事务");
    19. method.invoke(object);//调用目标对象中的sell方法
    20. System.out.println("之后提交事务");
    21. return proxy;
    22. }
    23. //根据目标对象生成代理对象
    24. public Object getProxy(){
    25. return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
    26. }
    27. }
    1. public class Test {
    2. public static void main(String[] args) {
    3. CarFactoryImpl carFactory = new CarFactoryImpl();
    4. DynamicProxy dtproxy = new DynamicProxy(carFactory);//创建动态代理器
    5. //这才是真正的创建动态代理对象 获取目标类所实现的接口
    6. Sell sell =(Sell)dtproxy.getProxy();//代理对象实现了目标对象的接口
    7. sell.sell();//使用代理对象调用接口中的方法,获取当前调用的方法,最终调用invoke方法
    8. }
    9. }

    (2)cglib代理

    针对jdk代理必须要让目标类实现接口这一问题,spring后来引入chlib技术。

    cglib代理:目标类不需要实现接口,在底层以生成字节码的方式,为目标类动态生成子类对象。在调用目标类方法时,对方法进行拦截,动态获取所调用的方法,最终动态调用方法。

    注意:cglib代理中,目标类不能用final修饰,目标类中的方法不能用final和static修饰。

    1. public class CarFactoryImpl {
    2. public void sell() {
    3. System.out.println("汽车厂卖汽车");
    4. }
    5. }
    1. //动态代理类
    2. public class CGLibProxy implements MethodInterceptor {
    3. private Enhancer enhancer = new Enhancer();
    4. public Object getProxy(Class clazz){
    5. enhancer.setSuperclass(clazz);
    6. enhancer.setCallback(this);
    7. return enhancer.create();//为目标类生成子类对象
    8. }
    9. /*
    10. * 拦截所有目标类方法的调用
    11. * 参数:
    12. * obj 目标实例对象
    13. * method 目标方法的反射对象
    14. * args 方法的参数
    15. * proxy 代理类的实例
    16. */
    17. public Object intercept(Object obj, Method method, Object[] args,
    18. MethodProxy proxy) throws Throwable {
    19. //代理类调用父类的方法
    20. System.out.println("开始事务");
    21. Object obj1 = proxy.invokeSuper(obj, args);
    22. System.out.println("关闭事务");
    23. return obj1;
    24. }
    25. }
    1. public class Test {
    2. public static void main(String[] args) {
    3. CGLibProxy proxy = new CGLibProxy();
    4. CarFactoryImpl carFactory = (CarFactoryImpl) proxy.getProxy(CarFactoryImpl.class);//为目标对象生成代理对象
    5. carFactory.sell();
    6. }
    7. }

  • 相关阅读:
    假如remake...普通人的碎碎念
    【Spring框架】——2. Spring IOC
    LightDB中的存储过程(三)
    反向传播不香了?解读 Hinton 大佬的 Forward-Forward 算法
    CUDA优化之PReLU性能调优
    目标检测论文解读复现之十:基于YOLOv5的遥感图像目标检测
    加满油箱问题
    《Python编程:从入门到实践》第十章练习题
    做这么多年程序员了,才把ELK和springboot的日志解决方案弄明白
    哆啦a梦教你页面的转发与重定向
  • 原文地址:https://blog.csdn.net/m0_73503454/article/details/134399408