java中常见的设计模式

news2024/11/27 22:45:43

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

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

为什么学习设计模式?

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

1、单例模式

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

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

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

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

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

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

Runtime.getRuntime();

2.懒汉式

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

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

线程安全问题的解决:

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

public class Window {
    //在类加载的时候不创建改类的对象,在第一次获取单例对象时创建 
    private static  Window  window=null;
    private Window(){}

    public static  synchronized  Window getWindow(){
         if(window==null){
             window = new Window();
         }
        return window;
    }
}
public class Test {
    public static void main(String[] args) {
       for (int i = 0; i < 10; i++) {
            new Thread(()->{
                System.out.println(Window.getWindow());
            }).start();
        }
    }
}

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

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

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

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

2、工厂模式

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

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

1.简单工厂模式

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

该模式中包含的角色:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

public interface AbstractFactory {
    Car getCar();
    Phone getPhone();
}
public class AodiFactory implements  AbstractFactory{
    @Override
    public Car getCar() {
        return new AodiCar();
    }

    @Override
    public Phone getPhone() {
        return new AodiPhone();
    }
}
public class BmwFactory implements AbstractFactory{
    @Override
    public Car getCar() {
        return new BmwCar();
    }

    @Override
    public Phone getPhone() {
        return new BmwPhone();
    }
}
public interface Car {
	void run();
}
public interface Phone {
	void  call();
}
public class AodiCar implements Car{
    @Override
    public void run() {
        System.out.println("奥迪汽车行驶");
    }
}
public class AodiPhone implements Phone{
    @Override
    public void call() {
        System.out.println("奥迪手机打电话");
    }
}
public class Test {
    public static void main(String[] args) {
        AbstractFactory aodiFactory = new AodiFactory();
        Car aodiCar = aodiFactory.getCar();
        Phone aodiphone = aodiFactory.getPhone();
              aodiCar.run();
              aodiphone.call();

        AbstractFactory bmwFactory = new BmwFactory();
        Car bmwCar = bmwFactory.getCar();
        Phone bmwPhone = bmwFactory.getPhone();
              bmwCar.run();
              bmwPhone.call();
    }
}

3、原型模式

spring创建bean作用域:

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

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

4、代理模式

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

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

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

1.静态代理

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

角色:

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

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

public class StaticProxy implements SellCar,SellPhone{}

//抽象操作定义 卖东西
public interface Sell {
     void sell();
}
//汽车厂
public class CarFactoryImpl implements Sell {
    @Override
    public void sell() {
        System.out.println("汽车厂卖汽车");
    }
}
//静态代理,实际中很少使用静态代理,因为其代理类实现的接口必须与目标类实现接口一致,扩展起来就比较麻烦
public class StaticProxy  implements Sell {
    Sell sell;
    public StaticProxy(Sell sell) {
        this.sell = sell;
    }

    //代理对象中的方法
    @Override
    public void sell() {
        System.out.println("汽车介绍");
        sell.sell();//最终执行者还是目标对象
        System.out.println("办理手续");
    }
}
public class Test {
    public static void main(String[] args) {
        Sell carFactory = new CarFactoryImpl();
        //创建汽车厂代理对象
        StaticProxy staticProxy = new StaticProxy(carFactory);
        staticProxy.sell();//看似访问的是代理类,其实是目标对象来做的
    }
}
/*
汽车介绍
汽车厂卖汽车
办理手续 
*/
2.动态代理

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

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

(1)jdk代理

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

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

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

//抽象操作定义 卖东西
public interface Sell {
     void sell();
}
//汽车厂
public class CarFactoryImpl implements Sell {
    @Override
    public void sell() {
        System.out.println("汽车厂卖汽车");
    }
}
/*
  动态代理类
  代理类不需要实现与目标类相同的接口,这样就可以代理任意的目标类
  但是是有要求的,目标类必需实现接口,此种方式是动态代理的实现方式之一: jdk代理 是一种纯反射机制实现(动态获取目标类接口方法)
 */
public class DynamicProxy implements InvocationHandler {

     Object object;//真实对象,接收任何的目标类对象

    public DynamicProxy(Object object) {
        this.object = object;
    }

    /*
       在代理类中调用目标类中的具体方法,
       动态的将代理动态对象,目标类中要调用的方法,及方法中的参数传递过来
       Method method  动态获取的真正要执行的方法
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("之前开启事务");
        method.invoke(object);//调用目标对象中的sell方法
        System.out.println("之后提交事务");
        return proxy;
    }

    //根据目标对象生成代理对象
    public Object getProxy(){
          return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
    }
}
public class Test {
    public static void main(String[] args) {
          CarFactoryImpl carFactory = new CarFactoryImpl();
          DynamicProxy dtproxy =  new DynamicProxy(carFactory);//创建动态代理器
          //这才是真正的创建动态代理对象   获取目标类所实现的接口
          Sell sell =(Sell)dtproxy.getProxy();//代理对象实现了目标对象的接口
          sell.sell();//使用代理对象调用接口中的方法,获取当前调用的方法,最终调用invoke方法
    }
}

(2)cglib代理

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

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

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

public  class CarFactoryImpl {
    public  void sell() {
        System.out.println("汽车厂卖汽车");
    }
}
//动态代理类
public class CGLibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();

    public Object getProxy(Class<?> clazz){  
        enhancer.setSuperclass(clazz);  
        enhancer.setCallback(this);  
        return enhancer.create();//为目标类生成子类对象
    }  
    /*
     * 拦截所有目标类方法的调用 
     * 参数: 
     * obj  目标实例对象 
     * method 目标方法的反射对象 
     * args 方法的参数 
     * proxy 代理类的实例 
     */
    public Object intercept(Object obj, Method method, Object[] args,  
            MethodProxy proxy) throws Throwable {
        //代理类调用父类的方法  
        System.out.println("开始事务");  
        Object obj1 = proxy.invokeSuper(obj, args);  
        System.out.println("关闭事务");  
        return obj1;  
    }
}
public class Test {
	public static void main(String[] args) {
        CGLibProxy proxy = new CGLibProxy();
        CarFactoryImpl carFactory = (CarFactoryImpl) proxy.getProxy(CarFactoryImpl.class);//为目标对象生成代理对象
        carFactory.sell();
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1207545.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

终端神器:tmux

安装tmux简单使用自己的理解&#xff08;小白专属&#xff09; 使用的初衷&#xff1a; 在Linux终端下&#xff0c;由于session&#xff08;会话&#xff09;和windows&#xff08;窗口&#xff09;是绑定一起的&#xff0c;你打开一个终端的黑窗口就是打开一个会话&#xff0c…

sinc 函数

See https://wuli.wiki/online/sinc.html 公式(3)的证明见 https://wuli.wiki/online/JdLem.html#ex_JdLem_1 百度百科

git快速上传代码

① git init&#xff1b; 初始化git&#xff0c;之后在文件夹里有.git文件&#xff0c;这个需要 勾选才能查看。 ② git remote add test myFisrtTest: 测试专用 这里的test是自定义的&#xff0c;myFisrtTest: 测试专用 是远程仓库 ③ git branch -a 这里是查看分支 ④ …

好消息!2023年汉字小达人市级比赛在线模拟题大更新:4个组卷+11个专项,助力孩子更便捷、有效、有趣地备赛

自从《中文自修》杂志社昨天发通知&#xff0c;官宣了2023年第十届汉字小达人市级比赛的日期和安排后&#xff0c;各路学霸们闻风而动&#xff0c;在自己本就繁忙的日程中又加了一项&#xff1a;备赛汉字小达人市级比赛&#xff0c;11月30日&#xff0c;16点-18点。 根据这几年…

软磁直流测试系统全自动测量软件

软磁直流测试系统软件能够运行于 Windows 系统下作界面全中文提示&#xff0c;操作直观简捷。全自动控制与计算&#xff0c;智能化判断&#xff0c;最大限度消除人工操作所带来的误差。自动测量&#xff1a;Bm、Br、Hc、μi、μm 等静态磁特性参数&#xff1b;并绘制磁滞回线、…

记录:unity脚本的编写6.0

目录 unity UI系统添加ui编写脚本 unity UI系统 在日常的游戏或者别的什么活动中&#xff0c;ui总是必不可少的一项&#xff0c;在java中也有关于GUI的内容&#xff0c;unity也不例外&#xff0c;这次就使用脚本控制在unity添加的各种ui组件&#xff0c;使他们可以完成一些我们…

企业级真实应用利用Mybatis-Plus进行分页查询处理

怎么导入依赖我在之前的文章里边有说过不理解的可以看看 你应该懂点Mybatis-plus&#xff0c;真的好用 1&#xff1a;了解Page<T>类的使用 首先我们需要使用到Page类 &#xff0c;建立一个Page类&#xff0c;泛式类型中放入我们需要输出的类&#xff0c;是列表的话就…

(C语言)编写程序将一个4×4的数组进行顺时针旋转90度后输出。

要求&#xff1a;原始数组的数据从键盘随机输入&#xff0c;新数组以4行4列的方式输出。 #include<stdio.h> int main() {int matrix[4][4],matrix2[4][4];int count;for(int i 0;i < 4;i )for(int j 0;j < 4;j )scanf("%d",&matrix[i][j]);for(i…

HarmonyOS元服务实现今天吃什么

一、前言 作为一个职业打工人&#xff0c;每天点外卖吃啥东西都有选择综合症&#xff0c;突发奇想让程序帮我们随机选择一个吃的&#xff0c;是不是可以解决我们的选择问题呢&#xff0c;说干就干&#xff0c;我们就使用HarmonyOS元服务实现一下这个功能。为什么选择这个Harmon…

Open X-Embodiment 超大规模开源真实机器人数据集分享

近期&#xff0c;Google旗下的前沿人工智能企业DeepMind汇集了来自 22 种不同机器人类型的数据&#xff0c;创建了 Open X-Embodiment 数据集并开源了出来。该数据集让他们研发的RT-2 机器人在制造和编程方式上有了重大飞跃。 有分析称&#xff0c;在上述数据集上训练的 RT-2-…

每天一点python——day67

#每天一点Python——67 #字符串判断方法&#xff1a;如图&#xff1a; #①判断指定字符串是否为合法标识符 shello,computer print(s.isidentifier()) #输出为False&#xff0c;不是合法标识符&#xff0c;这是因为标识符是由字母&#xff0c;数字&#xff0c;下划线组成&#…

工业数据的“最后一公里”怎么走?

随着工业互联网的迅猛发展&#xff0c;工业数据已经成为推动制造业转型升级的重要动力。然而&#xff0c;面对海量的工业数据&#xff0c;如何高效、准确地走过数据的“最后一公里”&#xff0c;成为制约企业发展的关键问题。本文将探讨工业数据“最后一公里”所面临的挑战&…

15.“百钱百鸡“问题

文章目录 前言一、题目描述 二、题目分析 三、解题 程序运行代码1程序运行代码优化程序运行代码优化进阶 前言 本系列为结构循环编程题&#xff0c;点滴成长&#xff0c;一起逆袭。 一、题目描述 二、题目分析 三、解题 程序运行代码1 #include <stdio.h> int main…

ARPG----C++学习记录05 Section10 武器类,IK重定向,装备和捡起武器,动画蓝图

代码更新 11.13 BAOfanTing/ARPG_Game_Code7ab54d2 GitHub 武器类 基于item类&#xff0c;创建一个weapon的C类&#xff0c;基于它创建一个蓝图&#xff0c;刀剑的网格体给它。在蓝图里调动之前在C写好的sin函数添加到世界偏移量里&#xff0c;得到一把悬浮刀 在item把重叠函…

Win10系统把D盘空间分给C盘的方法

在Win10系统中&#xff0c;用户发现C盘的空间太少了&#xff0c;导致电脑运作出现了卡顿的问题&#xff0c;所以想把D盘的空间分给C盘&#xff0c;但不清楚具体的操作步骤。接下来小编给大家介绍两种简单的操作方法&#xff0c;帮助大家轻松将Win10系统D盘的空间分给C盘&#x…

​Unity Vuforia 新手(图片识别)教程,后续整理 实体识别 详细流程

文章目录 前言一、Vuforia是什么&#xff1f;二、Unity导入Vuforia1.去Unity - Windows – Asset Store&#xff0c;搜vuforia engine&#xff0c;添加到我的资源2.打开package Manager&#xff0c;导入到工程中即可3.或者在vuforia engine官网下载的Unity包导入4.检查是否导入…

mysql数据库时间

记录MySQL今天又一个新的问题&#xff1a; 场景&#xff1a;nodejs后台容器部署 问题原因&#xff1a;纯属好心办坏事&#xff0c;由于考虑了时区&#xff08;现在看来纯属多余&#xff09;&#xff0c;在写入时间时使用了time_str.toLocaleString("chinese", { ti…

android studio开发flutter应用,使用mumu模拟器调试软件

安装好mumu模拟器&#xff0c;先打开网易mumu模拟器的开发者模拟。系统应用 > 设置 > 关于手机 > 版本号 多点击几次调出开发者模式&#xff1a; 然后在android studio中刷新设备列表&#xff0c;就能看到新设备了&#xff1a; 如何确定这个设备就是你的mumu模拟器呢…

Cesium:绘制点

作者&#xff1a;CSDN _乐多_ 本文记录了在Cesium中绘制点的函数。 文章目录 data() 里面设置点的样式 pointOption: {show: true,pixelSize: 10,heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,color: Cesium.Color.RED,outlineColor: Cesium.Color.YELLOW,outl…

视频封装格式

FLV&#xff08;Flash Video&#xff09; FLV封装格式 Tag Data分为Audio&#xff0c;Video&#xff0c;Script三种 TS&#xff08;Transport Stream&#xff09;传输流 TS文件分为三层&#xff0c;&#xff08;倒叙更好理解&#xff09; TS层&#xff1a;在PES层基础上加入…