java设计模式之结构型模式(7种)

结构型模式

描述如何将类或者对象按某种布局组成更大的结构。它分为结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者通过组合或聚合来组合对象。
分为7种:代理模式、适配器模式、装饰者模式、桥接模式、外观模式、组合模式、享元模式。

代理模式

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象访问对象和目标对象之间的中介
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时候动态生成。动态代理又有JDK代理和CGLIb代理两种。
在这里插入图片描述

静态代理
在这里插入图片描述
卖火车票的接口

public interface SellTickets {void sell();
}

火车站类

public class TrainStation implements SellTickets {public void sell() {System.out.println("火车站卖票");}
}

代售点类:代理火车站,进行卖票操作,同时对该方法做了点增强(收取费用)

public class ProxyPoint implements SellTickets {// 火车站类对象private TrainStation trainStation  = new TrainStation();public void sell() {System.out.println("代售点收取一些服务费用");trainStation.sell();}
}

测试类

public class Client {public static void main(String[] args) {// 创建代售点类对象ProxyPoint proxyPoint = new ProxyPoint();// 调用方法进行卖票proxyPoint.sell();}
}

动态代理
JDK动态代理
Java 中提供了一个动态代理类 Proxy,Proxy 并不是我们上述所说的代理对象的类,而是提供了一个创建代理对象的静态方法(newProxyInstance 方法)来获取代理对象。

public interface SellTickets {void sell();
}
public class TrainStation implements SellTickets {public void sell() {System.out.println("火车站卖票");}
}

获取代理对象的工厂类:

public class ProxyFactory {// 目标对象private TrainStation station = new TrainStation();// 获取代理对象public SellTickets getProxyObject() {// 返回代理对象/*ClassLoader loader: 类加载器,用于加载代理类,可以通过目标对象获取类加载器Class<?>[] interfaces: 代理类实现的接口的字节码对象InvocationHandler h: 代理对象调用处理程序*/SellTickets proxyObject = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(), // 类加载器,用于加载代理类station.getClass().getInterfaces(), // 代理类实现的接口的字节码对象new InvocationHandler() {/*** @param proxy 代理对象, 和proxyObject是同一个对象,在invoke方法中基本不用* @param method 对接口中的方法进行封装的method对象* @param args 调用方法的实际参数* @return 方法的返回值*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代售点收取一定的服务费用(jdk动态代理 )");// 执行目标对象的方法Object obj = method.invoke(station, args);return obj;}});return proxyObject;}
}

测试类

public class Client {public static void main(String[] args) {// 获取代理对象// 1, 创建代理工厂对象ProxyFactory factory = new ProxyFactory();// 2, 使用factory对象的方法获取代理对象SellTickets proxyObject = factory.getProxyObject();// 3, 调用代理对象的方法proxyObject.sell();}
}

CGLIB 动态代理
上面的案例中,如果没有定义 SellTickets 接口,只定义了 TrainStation 火车站类。则 JDK 代理无法使用,因为 JDK 动态代理要求必须定义接口,它只能对接口进行代理。
CGLIB 是一个功能强大,高性能的代码生成包,它可以为没有实现接口的类提供代理,为 JDK 的动态代理提供了很好的补充。CGLIB 是第三方提供的包,所以需要引入依赖:

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2.2</version>
</dependency>

代码如下:CGLIB 动态代理可以不代理接口,直接代理类。
火车站类:火车票具有卖票功能,实现 SellTickets 接口,同静态代理。

public class TrainStation implements SellTickets {public void sell() {System.out.println("火车站卖票");}
}

代理工厂类:用来获取代理对象

/*** 代理对象工厂, 用来获取代理对象*/
public class ProxyFactory implements MethodInterceptor {// 火车站对象private TrainStation station = new TrainStation();// 获取代理对象public TrainStation getProxyObject() {// 创建Enhancer对象,类似于JDK代理中的Proxy类Enhancer enhancer = new Enhancer();// 设置父类的字节码对象(指定父类)enhancer.setSuperclass(TrainStation.class);// 设置回调函数enhancer.setCallback(this);// 创建代理对象TrainStation proxyObject = (TrainStation) enhancer.create();return proxyObject;}/**** @param o 代理对象* @param method 真实对象中的方法的Method实例* @param objects 实际参数* @param methodProxy 代理对象中的方法的method实例* @return 方法执行结果*/@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("代售点收取一定的服务费用(Cglib代理)");// 要调用目标对象的方法Object invoke = method.invoke(station, objects);return invoke;}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

适配器模式

在这里插入图片描述

将一个类的接口转换为客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
适配器模式分为:
类结构型模式:采用继承机制来组织接口和类。
对象结构型模式:釆用组合或聚合来组合对象。
前者由于类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
在这里插入图片描述

类适配器模式

定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。
在这里插入图片描述
Computer:可以直接访问 SDCard,但是不能直接访问 TFCard,即将通过适配器去访问 TFCard

public class Computer {// 从SD卡中读取数据public String readSD(SDCard sdCard) {if (sdCard == null) {throw new NullPointerException("sd card can not be null");}return sdCard.readSD();}
}

目标接口:SDCard 是 Computer 期望访问的接口

/*** SD卡的接口*/
public interface SDCard {// 从SD卡中读取数据String readSD();// 往SD卡中写数据void writeSD(String msg);
}/*** SD卡的实现类*/
public class SDCardImpl implements SDCard {public String readSD() {String msg = "sd card read a msg :hello word SD";return msg;}public void writeSD(String msg) {System.out.println("sd card write msg : " + msg);}
}

适配者类:TFCard 是要被适配的接口,适配成可以通过 SDCard 来访问

/*** TF卡的接口*/
public interface TFCard {// 从TF卡中读取数据String readTF();// 往TF卡中写数据void writeTF(String msg);
}/*** TF卡的实现类*/
public class TFCardImpl implements TFCard {public String readTF() {String msg = "TFCard read msg: hello word TFCard";return msg;}public void writeTF(String msg) {System.out.println("TFCard write msg: " + msg);}
}

适配器类:实现业务接口,继承已有组件
本质上还是要执行 TFCard 的功能,因此 extends TFCardImpl
但是又可以让 Computer 通过 SDCard 接口来访问,因此 implements SDCard

public class SDAdapterTF extends TFCardImpl implements SDCard {public String readSD() {System.out.println("adapter read tf card");return readTF();}public void writeSD(String msg) {System.out.println("adapter write tf card");writeTF(msg);}
}

测试类:

public class Client {public static void main(String[] args) {// 创建计算机对象Computer computer = new Computer();// 电脑读取SD卡中的数据(可直接读)String msg = computer.readSD(new SDCardImpl());System.out.println(msg);System.out.println("===============");// 使用电脑读取TF卡中的数据(无法直接读)// 定义适配器类String msg1 = computer.readSD(new SDAdapterTF());System.out.println(msg1);}
}

类适配器模式违背了合成复用原则,类适配器是客户类有一个接口规范的情况下可用,反之不可用。

对象适配器模式

实现方式:对象适配器模式可釆用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。
在这里插入图片描述
代码如下:类适配器模式的代码,我们只需要修改适配器类(SDAdapterTF)和测试类

适配器类:通过聚合一个适配者类,来使用它的方法

public class SDAdapterTF implements SDCard {// 声明适配者类private TFCard tfCard;public SDAdapterTF(TFCard tfCard) {this.tfCard = tfCard;}public String readSD() {System.out.println("adapter read tf card");return tfCard.readTF();}public void writeSD(String msg) {System.out.println("adapter write tf card");tfCard.writeTF(msg);}
}

测试类:

public class Client {public static void main(String[] args) {// 创建计算机对象Computer computer = new Computer();// 电脑读取SD卡中的数据(可直接读)String msg = computer.readSD(new SDCardImpl());System.out.println(msg);System.out.println("===============");// 使用电脑读取TF卡中的数据(无法直接读)// 创建适配器类对象SDAdapterTF sdAdapterTF = new SDAdapterTF(new TFCardImpl());String msg1 = computer.readSD(sdAdapterTF);System.out.println(msg1);}
}

接口适配器模式

当不希望实现一个接口中所有的方法时,可以创建一个抽象类 Adapter 实现接口所有方法,然后只需要继承该抽象类即可。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

装饰者模式

在这里插入图片描述
在这里插入图片描述
定义:指在不改变现有对象结构的情况下,动态的给该对象增加一些职责(即增加其额外功能)的模式。
在这里插入图片描述
在这里插入图片描述
抽象构件角色:快餐类

@Data
public abstract class FastFood {private float price; // 价格private String desc; // 描述public FastFood(float price, String desc) {this.price = price;this.desc = desc;}public abstract float cost();
}

具体构件角色:炒饭类、炒面类

public class FriedRice extends FastFood {public FriedRice() {super(10, "炒饭");}public float cost() {return getPrice();}
}public class FriedNoodles extends FastFood {public FriedNoodles() {super(12, "炒面");}public float cost() {return getPrice();}
}

抽象装饰者角色:配料类

@Data
public abstract class Garnish extends FastFood {// 声明快餐类的变量private FastFood fastFood;public Garnish(FastFood fastFood, float price, String desc) {super(price, desc);this.fastFood = fastFood;}
}

具体装饰者角色:鸡蛋配料类、培根配料类

public class Egg extends Garnish {public Egg(FastFood fastFood) {super(fastFood, 1, "鸡蛋");}// 计算价格public float cost() {return getPrice() + getFastFood().cost();}@Overridepublic String getDesc() {return super.getDesc() + getFastFood().getDesc();}
}public class Bacon extends Garnish {public Bacon(FastFood fastFood) {super(fastFood, 2, "培根");}// 计算价格public float cost() {return getPrice() + getFastFood().cost();}@Overridepublic String getDesc() {return super.getDesc() + getFastFood().getDesc();}
}

测试类:

public class Client {public static void main(String[] args) {//  点一份炒饭FastFood food = new FriedRice();System.out.println(food.getDesc() + "  " + food.cost() + "元");// 在上面的炒饭中加一个鸡蛋food = new Egg(food);System.out.println(food.getDesc() + "  " + food.cost() + "元");// 再加一个鸡蛋food = new Egg(food);System.out.println(food.getDesc() + "  " + food.cost() + "元");// 再加一个培根food = new Bacon(food);System.out.println(food.getDesc() + "  " + food.cost() + "元");}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
BufferedWriter 使用装饰者模式对 Writer 子实现类进行了增强,添加了缓冲区,提高了写数据的效率。
在这里插入图片描述

桥接模式

在这里插入图片描述
定义:将抽象和实现分离,使它们可以独立变化。它是用组合关系来代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
在这里插入图片描述
在这里插入图片描述
实现化角色:视频文件格式接口

public interface VideoFile {// 解码功能void decode(String fileName);
}

具体实现化角色:具体的视频文件类,avi、rmvb 等

public class AviFile implements VideoFile {public void decode(String fileName) {System.out.println("avi视频文件 :" + fileName);}
}public class RmvbFile implements VideoFile {public void decode(String fileName) {System.out.println("rmvb视频文件 :" + fileName);}
}

抽象化角色:操作系统抽象类

public abstract class OperatingSystem {// 声明videFile变量protected VideoFile videoFile;public OperatingSystem(VideoFile videoFile) {this.videoFile = videoFile;}public abstract void play(String fileName);
}

拓展抽象化角色:Windows、Mac 操作系统

public class Windows extends OperatingSystem {public Windows(VideoFile videoFile) {super(videoFile);}public void play(String fileName) {videoFile.decode(fileName);}
}public class Mac extends OperatingSystem {public Mac(VideoFile videoFile) {super(videoFile);}public void play(String fileName) {videoFile.decode(fileName);}
}

测试类:

public class Client {public static void main(String[] args) {// 创建Mac系统对象OperatingSystem system = new Mac(new AviFile());// 使用操作系统播放视频文件system.play("战狼3");}
}

在这里插入图片描述

外观模式

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
子系统角色:电灯、电视、空调

public class Light {public void on() {System.out.println("打开电灯。。。。");}public void off() {System.out.println("关闭电灯。。。。");}
}public class TV {public void on() {System.out.println("打开电视机。。。。");}public void off() {System.out.println("关闭电视机。。。。");}
}public class AirCondition {public void on() {System.out.println("打开空调。。。。");}public void off() {System.out.println("关闭空调。。。。");}
}

外观角色:智能音箱,用于控制各个子系统,用户主要和该对象进行交互

public class SmartAppliancesFacade {// 聚合电灯对象,电视机对象,空调对象private Light light;private TV tv;private AirCondition airCondition;public SmartAppliancesFacade() {light = new Light();tv = new TV();airCondition = new AirCondition();}// 通过语言控制public void say(String message) {if (message.contains("打开")) {on();} else if (message.contains("关闭")) {off();} else {System.out.println("我还听不懂你说的!!!");}}// 一键打开功能private void on() {light.on();tv.on();airCondition.on();}// 一键关闭功能private void off() {light.off();tv.off();airCondition.off();}
}

测试类:

public class Client {public static void main(String[] args) {// 创建智能音箱对象SmartAppliancesFacade facade = new SmartAppliancesFacade();// 控制家电facade.say("打开家电");facade.say("关闭家电");}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

组合模式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
不管是菜单还是菜单项,都应该继承自统一的接口,这里将这个统一的接口称为菜单组件。

/*** 菜单组件:属于抽象根节点*/
public abstract class MenuComponent {// 菜单组件的名称protected String name;// 菜单组件的层级protected int level;// 添加子菜单public void add(MenuComponent menuComponent) {throw new UnsupportedOperationException();}// 移除子菜单public void remove(MenuComponent menuComponent) {throw new UnsupportedOperationException();}// 获取指定的子菜单public MenuComponent getChild(int index) {throw new UnsupportedOperationException();}// 获取菜单或者菜单项的名称public String getName() {return name;}// 打印菜单名称的方法(包含子菜单和字菜单项)public abstract void print();
}

Menu 类实现了除了 getName 方法的其他所有方法,因为 Menu 类具有添加菜单,移除菜单和获取子菜单的功能。

/*** 菜单类:属于树枝节点*/
public class Menu extends MenuComponent {// 菜单可以有多个子菜单或者子菜单项private List<MenuComponent> menuComponentList = new ArrayList<>();public Menu(String name, int level) {this.name = name;this.level = level;}@Overridepublic void add(MenuComponent menuComponent) {menuComponentList.add(menuComponent);}@Overridepublic void remove(MenuComponent menuComponent) {menuComponentList.remove(menuComponent);}@Overridepublic MenuComponent getChild(int index) {return menuComponentList.get(index);}@Overridepublic void print() {// 打印菜单名称for (int i = 0; i < level; i++) {System.out.print("--");}System.out.println(name);// 打印子菜单或者子菜单项名称for (MenuComponent component : menuComponentList) {component.print();}}
}

MenuItem 是菜单项,不能再有子菜单,所以添加菜单,移除菜单和获取子菜单的功能并不能实现。

/*** 菜单项类:属于叶子节点*/
public class MenuItem extends MenuComponent {public MenuItem(String name, int level) {this.name = name;this.level = level;}public void print() {// 打印菜单项的名称for (int i = 0; i < level; i++) {System.out.print("--");}System.out.println(name);}
}

测试类

public class Client {public static void main(String[] args) {// 创建二级菜单树MenuComponent menu1 = new Menu("菜单管理", 2);menu1.add(new MenuItem("页面访问", 3));menu1.add(new MenuItem("展开菜单", 3));menu1.add(new MenuItem("编辑菜单", 3));menu1.add(new MenuItem("删除菜单", 3));menu1.add(new MenuItem("新增菜单", 3));MenuComponent menu2 = new Menu("权限管理", 2);menu2.add(new MenuItem("页面访问", 3));menu2.add(new MenuItem("提交保存", 3));MenuComponent menu3 = new Menu("角色管理", 2);menu3.add(new MenuItem("页面访问", 3));menu3.add(new MenuItem("新增角色", 3));menu3.add(new MenuItem("修改角色", 3));// 创建一级菜单MenuComponent component = new Menu("系统管理", 1);// 将二级菜单添加到一级菜单中component.add(menu1);component.add(menu2);component.add(menu3);// 打印菜单名称(如果有子菜单一块打印)component.print();}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

享元模式

运用共享技术来有效地支持大量细粒度对象的复用,通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似对象的开销,从而提高系统资源的利用率。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
俄罗斯方块有不同的形状,可以对这些形状向上抽取出 AbstractBox,用来定义共性的属性和行为。

/*** 抽象享元角色*/
public abstract class AbstractBox {// 获取图形的方法public abstract String getShape();// 显示图形及颜色public void display(String color) {System.out.println("方块形状:" + getShape() + ", 颜色:" + color);}
}

IBox 类、LBox 类、OBox 类等。

/*** 具体享元角色*/
public class IBox extends AbstractBox {public String getShape() {return "I";}
}
public class LBox extends AbstractBox {public String getShape() {return "L";}
}
public class OBox extends AbstractBox {public String getShape() {return "O";}
}

提供一个工厂类 BoxFactory,用来管理享元对象(也就是 AbstractBox 子类对象),该工厂类对象只需要一个,所以可以使用单例模式,并给工厂类提供一个获取形状的方法。

public class BoxFactory {private static BoxFactory factory = new BoxFactory();private HashMap<String, AbstractBox> map;// 在构造方法中进行初始化操作private BoxFactory() {map = new HashMap<>();map.put("I", new IBox());map.put("L", new LBox());map.put("O", new OBox());}// 提供一个方法获取该工厂类对象public static BoxFactory getInstance() {return factory;}// 根据名称获取图形对象public AbstractBox getShape(String name) {return map.get(name);}
}

测试类:

public class Client {public static void main(String[] args) {// 获取I图形对象AbstractBox box1 = BoxFactory.getInstance().getShape("I");box1.display("灰色");// 获取L图形对象AbstractBox box2 = BoxFactory.getInstance().getShape("L");box2.display("绿色");// 获取O图形对象AbstractBox box3 = BoxFactory.getInstance().getShape("O");box3.display("灰色");// 获取O图形对象AbstractBox box4 = BoxFactory.getInstance().getShape("O");box4.display("红色");System.out.println("两次获取到的O图形对象是否是同一个对象:" + (box3 == box4));}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
valueof()方法:

public final class Integer extends Number implements Comparable<Integer> {public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);}private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {int h = 127;String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh = Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch( NumberFormatException nfe) {}}high = h;cache = new Integer[(high - low) + 1];int j = low;for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);// range [-128, 127] must be interned (JLS7 5.1.7)assert IntegerCache.high >= 127;}private IntegerCache() {}}
}

在这里插入图片描述

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

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

相关文章

Java序列化与反序列化

文章目录 一、Java序列化和反序列化1、序列化和反序列化的含义和用途序列化主要使用场景反序列化漏洞出现的原因 下一期 一、Java序列化和反序列化 1、序列化和反序列化的含义和用途 Java对象&#xff08;存在于内存&#xff09;———序列化——>>字符串/二进制流&…

Vue computed watch

computed watch watch current prev

基于Spring Boot+Vue的助农销售平台(协同过滤算法、限流算法、支付宝沙盒支付、实时聊天、图形化分析)

&#x1f388;系统亮点&#xff1a;协同过滤算法、节流算法、支付宝沙盒支付、图形化分析、实时聊天&#xff1b; 一.系统开发工具与环境搭建 1.系统设计开发工具 后端使用Java编程语言的Spring boot框架 项目架构&#xff1a;B/S架构 运行环境&#xff1a;win10/win11、jdk1…

GetX的一些高级API

目录 前言 一、一些常用的API 二、局部状态组件 1.可选的全局设置和手动配置 2.局部状态组件 1.ValueBuilder 1.特点 2.基本用法 2.ObxValue 1.特点 2.基本用法 前言 这篇文章主要讲解GetX的一些高级API和一些有用的小组件。 一、一些常用的API GetX提供了一些高级…

第三届北京国际水利科技博览会将于25年3月在国家会议中心召开

由中国农业节水和农村供水技术协会、北京水利学会、振威国际会展集团等单位联合主办的第三届北京国际水利科技博览会暨供水技术与设备展&#xff08;北京水利展&#xff09;将于2025年3月31日至4月2日在北京•国家会议中心举办&#xff01; 博览会以“新制造、新服务、新业态”…

基于SpringBoot的学生读书笔记共享的设计与实现

一、项目背景 计算机的普及和互联网时代的到来使信息的发布和传播更加方便快捷。用户可以通过计算机上的浏览器访问多个应用系统&#xff0c;从中获取一些可以满足用户需求的管理系统。网站系统有时更像是一个大型“展示平台”&#xff0c;用户可以选择所需的信息进入系统查看…

org.springframework.boot:type=Admin,name=SpringApplication异常

org.springframework.boot:typeAdmin,nameSpringApplication异常 问题&#xff1a;更换最新版本idea之后&#xff0c;启动springboot项目报错 javax.management.InstanceNotFoundException: org.springframework.boot:typeAdmin,nameSpringApplication idea自动默认的启动设…

Netty核心源码与优化

1.Netty的优化 1.1 使用EventLoop的任务调度 直接使用 channel.writeAndFlush(data) 可能会导致线程切换&#xff0c;这是因为如果当前线程并不是该 Channel 所绑定的 EventLoop 线程&#xff0c;那么 writeAndFlush() 操作会将任务重新提交给关联的 EventLoop 线程执行&#…

CTF之web题集详情随手笔记

《Web安全》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484238&idx1&snca66551c31e37b8d726f151265fc9211&chksmc0e47a12f793f3049fefde6e9ebe9ec4e2c7626b8594511bd314783719c216bd9929962a71e6&scene21#wechat_redirect 1 WEB 1 靶场目…

使用Nginx作为反向代理和负载均衡器

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Nginx作为反向代理和负载均衡器 引言 Nginx 简介 安装 Nginx Ubuntu CentOS 配置 Nginx 作为反向代理 配置 Nginx 作为负载…

【PTA】图的邻接矩阵存储和遍历

图的邻接矩阵存储用一个一维数组存储各顶点数据元素&#xff0c;一个二维数组存储顶点之间的邻接关系。 如上面的无向加权图&#xff0c;顶点数据元素为“A-Z”之间的单个字符&#xff0c;为了使遍历输出结果唯一&#xff0c;要求顶点数据元素按由小到大(ASCII码)的顺序存储。…

数据结构之树

1.树的基本概念 1.树的定义 树是由n(n>0)个结点&#xff08;或元素&#xff09;组成的有限集合&#xff08;记为T&#xff09;。 如果n0,它是一棵空树&#xff0c;这是树的特例。 如果n>0&#xff0c;这个结点中有且仅有一个结点作为树的根结点&#xff0c;简称为根。…

国内PLC市场份额报告,西门子老大的地位从未动摇

【导读】国内PLC市场占有率&#xff0c;西门子依然是老大。 PLC市场集中度很高&#xff0c;从销售额来看&#xff0c;TOP3厂家占据一半以上的市场份额&#xff0c;以外资品牌为主&#xff0c;其中西门子排名第一&#xff0c;2022年市场份额约47.1%&#xff1b;三菱排名第二&…

计算机毕业设计 | 基于SpringBoot的健身房管理系统(附源码)

1&#xff0c;项目背景 随着人们生活水平的提高和健康意识的增强&#xff0c;健身行业逐渐兴起并迅速发展。而现代化的健身房管理系统已经成为健身房发展的必备工具之一。传统的健身房管理方式已经无法满足现代化健身房的需求&#xff0c;需要一种更加高效、智能、安全的管理系…

ADI常规SHARC音频处理器性能对比

1、 ADSP-2156x:是基于SHARC+ DSP架构的单核32位/40位/64位浮点处理器,不仅具有灵活的音频连接性和性能可扩展性,还提供多个引脚兼容版本(400MHz至1GHz)和多种片内存储器选项,数据手册链接:https://www.analog.com/media/en/technical-documentation/data-sheets/adsp-2…

PG实践|条件处理表达式函数及其操作

文章目录 引言条件处理表达式1.1 GREATEST() 和 LEAST() - 最大值和最小值1.2 CASE() - 万一/具体情况为1.3 COALESCE - 处理NULL1.4 NULLIF - 处理NULL 总结 &#x1f4eb; 作者简介&#xff1a;「六月暴雪飞梨花」&#xff0c;专注于研究Java&#xff0c;就职于科技型公司后端…

跨境行业的客户运营利器:构建在线培训知识库

在跨境行业中&#xff0c;客户运营是企业成功的关键。随着全球市场的不断扩大和数字化转型的深入&#xff0c;构建一个有效的在线培训知识库对于提升客户服务质量、优化客户体验至关重要。本文将探讨在线培训知识库的构建策略、应用价值&#xff0c;并分析其在跨境行业的实际应…

C语言另一种编码方式开发状态机(无switch)

目录 概述 一、开发环境 二、coding 三、运行结果 四、总结 概述 状态机编码思想&#xff0c;在很多领域都随处可见&#xff0c;传统的coding方式使用switch case来实现&#xff0c;状态跳转可读性差&#xff0c;咋们换一种思路来实现状态机开发&#xff0c;该方式 拓展好…

Android 面试Socket与WebSocket

Android 面试Socket与WebSocket 文章目录 Android 面试Socket与WebSocketSocket什么是SocketSocket通讯的过程TCP协议Socket代码示例UDP协议Socket代码示例Socket实现Android跨进程通信 WebSocket什么是websocket Socket 什么是Socket 网络上的两个程序通过以一个双向通讯的链…

python通过pyarmor库保护源代码

pyarmor 介绍 python是解释型语言&#xff0c;因此&#xff0c;代码不需要编译&#xff0c;而是通过明文提供的。在这种情况下&#xff0c;假如你发布了一个软件&#xff0c;那么&#xff0c;只能将源代码提供给使用者&#xff0c;这样就造成了源代码泄露。 使用pyinstaller…