【设计模式——学习笔记】23种设计模式——原型模式Prototype(原理讲解+应用场景介绍+案例介绍+Java代码实现)

原型模式

介绍

  • 原型模式指用通过拷贝原型实例创建新的实例,新实例和原型实例的属性完全一致
  • 原型模式是一种创建型设计模式
  • 工作原理是通过调用原型实例的 clone()方法来完成克隆,原型实例需要实现Cloneable接口,并重写clone()方法
  • 需要为每个类开发一个克隆方法,这对全新的类来说不难,但对已有类进行改造时,需要修改其源代码,违背了ocp原则
  • 实现深拷贝的时候可能需要比较复杂的代码

应用场景

在以下情况下,我们就不能根据new 类名()来生成实例,而是克隆现有实例来生成新实例

  • 对象种类繁多,无法将它们整合到一个类中(在软件设计中,有时候我们需要创建一种类型的对象,但是该类型下有多个具体的变体,这些变体之间可能存在大量差异,无法将这些变体都封装到一个类中。这时候可以使用原型模式,将这些变体作为原型,通过对原型进行克隆来得到新的对象。这样既避免了创建过多的分类,又能够保持对象的个性化)
  • 难以通过new来创建实例(有的实例非常复杂,想要创建一个一模一样的示例非常困难)
  • 想解耦框架与生成的实例时(想要让生成实例的框架不依赖于具体的类。这时,不能指定类名来生成实例,而要事先“注册”一个“原型”实例,然后通过复制该实例来生成新的实例)

类图

在这里插入图片描述

  • Prototype : 原型类,声明一个克隆自身的接口
  • ConcretePrototype: 具体的原型类,实现克隆方法
  • Client:调用原型对象的克隆方法,让一个原型对象克隆自身来创建一个属性一样的新对象

案例分析

克隆羊问题

现在有一只羊叫做tom,其属性为姓名: tom,年龄: 1,颜色:白色,请编写程序创建和 tom 属性完全相同的10只羊。

传统实现

package com.atguigu.prototype;public class Sheep {private String name;private int age;private String color;public Sheep(String name, int age, String color) {super();this.name = name;this.age = age;this.color = color;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}@Overridepublic String toString() {return "Sheep [name=" + name + ", age=" + age + ", color=" + color + "]";}}
package com.atguigu.prototype;public class Client {public static void main(String[] args) {// TODO Auto-generated method stub//传统的方法Sheep sheep = new Sheep("tom", 1, "白色");Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());//....System.out.println(sheep);System.out.println(sheep2);System.out.println(sheep3);System.out.println(sheep4);System.out.println(sheep5);//...}}

分析

【优点】

  • 比较好理解,简单易操作

【缺点】

  • 在创建新的对象时,需要获取原始对象的属性,如果创建的对象比较复杂时,效率较低,不够灵活(原型对象的属性修改之后,如添加一个属性,需要往构造方法中添加参数)
  • 总是需要重新初始化对象,而不能动态地获得对象运行时的状态

【改进】

  • 使用原型模式:在Java中,Obiect类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力

原型模式(浅拷贝)

package com.atguigu.prototype.improve;/*** 继承Cloneable*/
public class Sheep implements Cloneable {private String name;private int age;private String color;private String address = "蒙古羊";/*** 是对象, 克隆是会如何处理*/public Sheep friend;public Sheep(String name, int age, String color) {super();this.name = name;this.age = age;this.color = color;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}@Overridepublic String toString() {return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", address=" + address + "]";}/*** 克隆该实例,使用默认的clone方法来完成** @return*/@Overrideprotected Object clone() {Sheep sheep = null;try {sheep = (Sheep) super.clone();} catch (Exception e) {System.out.println(e.getMessage());}return sheep;}}
package com.atguigu.prototype.improve;public class Client {public static void main(String[] args) {System.out.println("原型模式完成对象的创建");Sheep sheep = new Sheep("tom", 1, "白色");sheep.friend = new Sheep("jack", 2, "黑色");Sheep sheep2 = (Sheep)sheep.clone();Sheep sheep3 = (Sheep)sheep.clone();Sheep sheep4 = (Sheep)sheep.clone();Sheep sheep5 = (Sheep)sheep.clone();System.out.println("sheep2 =" + sheep2 + "sheep2.friend=" + sheep2.friend.hashCode());System.out.println("sheep3 =" + sheep3 + "sheep3.friend=" + sheep3.friend.hashCode());System.out.println("sheep4 =" + sheep4 + "sheep4.friend=" + sheep4.friend.hashCode());System.out.println("sheep5 =" + sheep5 + "sheep5.friend=" + sheep5.friend.hashCode());}}
原型模式完成对象的创建
sheep2 =Sheep [name=tom, age=1, color=白色, address=蒙古羊]sheep2.friend=1554874502
sheep3 =Sheep [name=tom, age=1, color=白色, address=蒙古羊]sheep3.friend=1554874502
sheep4 =Sheep [name=tom, age=1, color=白色, address=蒙古羊]sheep4.friend=1554874502
sheep5 =Sheep [name=tom, age=1, color=白色, address=蒙古羊]sheep5.friend=1554874502Process finished with exit code 0

【分析】

  • 根据输出,可以看到其他sheep的friend原型对象的friend是同一个对象(因为hashcode一样),原因:上面的代码是浅拷贝,浅拷贝并不会重新复制一个引用类型的对象出来,只是单纯将克隆对象的friend指向原型对象的friend对象,这样的坏处是,如果原型对象的friend发生了改变,克隆对象的friend也会改变,这样不能算是真正的克隆

原型模式(深拷贝)

package com.atguigu.prototype.deepclone;import java.io.Serializable;public class DeepCloneableTarget implements Serializable, Cloneable {private static final long serialVersionUID = 1L;private String cloneName;private String cloneClass;/*** 构造器* @param cloneName* @param cloneClass*/public DeepCloneableTarget(String cloneName, String cloneClass) {this.cloneName = cloneName;this.cloneClass = cloneClass;}/*** 因为该类的属性,都是String , 因此我们这里使用默认的clone完成即可* @return* @throws CloneNotSupportedException*/@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
package com.atguigu.prototype.deepclone;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;public class DeepProtoType implements Serializable, Cloneable {/*** 基本数据类型*/public String name;/*** 引用类型*/public DeepCloneableTarget deepCloneableTarget;public DeepProtoType() {super();}/*** 深拷贝 - 方式 1 使用clone 方法** @return* @throws CloneNotSupportedException*/@Overrideprotected Object clone() throws CloneNotSupportedException {//这里完成对基本数据类型(属性)和String的克隆Object deep = super.clone();//对引用类型的属性,进行单独处理DeepProtoType deepProtoType = (DeepProtoType) deep;deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();return deepProtoType;}/*** 深拷贝 - 方式2 通过对象的序列化实现 (推荐)* @return*/public Object deepClone() {/// 创建流对象// 字节输出流ByteArrayOutputStream bos = null;// 对象输出流ObjectOutputStream oos = null;ByteArrayInputStream bis = null;ObjectInputStream ois = null;try {//序列化bos = new ByteArrayOutputStream();oos = new ObjectOutputStream(bos);//当前这个对象以对象流的方式输出oos.writeObject(this);//反序列化bis = new ByteArrayInputStream(bos.toByteArray());ois = new ObjectInputStream(bis);DeepProtoType copyObj = (DeepProtoType) ois.readObject();return copyObj;} catch (Exception e) {e.printStackTrace();return null;} finally {//关闭流try {bos.close();oos.close();bis.close();ois.close();} catch (Exception e2) {System.out.println(e2.getMessage());}}}}

【客户端调用】

package com.atguigu.prototype.deepclone;public class Client {public static void main(String[] args) throws Exception {// TODO Auto-generated method stubDeepProtoType p = new DeepProtoType();p.name = "宋江";p.deepCloneableTarget = new DeepCloneableTarget("大牛", "小牛");//方式1 完成深拷贝DeepProtoType p2 = (DeepProtoType) p.clone();System.out.println("p.name=" + p.name + "; p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());System.out.println("p2.name=" + p.name + "; p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());//方式2 完成深拷贝DeepProtoType p3 = (DeepProtoType) p.deepClone();System.out.println("p3.name=" + p.name + "; p3.deepCloneableTarget=" + p3.deepCloneableTarget.hashCode());}}

【运行】

p.name=宋江; p.deepCloneableTarget=1554874502
p2.name=宋江; p2.deepCloneableTarget=1846274136
p3.name=宋江; p3.deepCloneableTarget=932172204Process finished with exit code 0

【分析】
可以看到三个对象的deepCloneableTarget的hashCode不同,因此克隆成功

字符串修饰问题

在这里插入图片描述

【框架】

package framework;import java.lang.Cloneable;public interface Product extends Cloneable {/*** 实例使用* @param s*/public abstract void use(String s);/*** 实例复制* @return*/public abstract Product createClone();
}
package framework;import java.util.*;public class Manager {/*** 存储实例名及实例*/private HashMap showcase = new HashMap();/*** 注册示例* @param name* @param proto*/public void register(String name, Product proto) {showcase.put(name, proto);}/*** 根据实例名称使用实例* @param protoname* @return*/public Product create(String protoname) {Product p = (Product) showcase.get(protoname);return p.createClone();}
}

【实现类】

import framework.*;/*** use 方法的作用是将字符串用双引号括起来显示,并在字符串下面加上下划线。例如,当 ulchar 保存的字符为'~' 方法接收到的字符串为Hello时,显示结果如下** "Hellor"*  ~~~~~~*/
public class UnderlinePen implements Product {private char ulchar;public UnderlinePen(char ulchar) {this.ulchar = ulchar;}public void use(String s) {int length = s.getBytes().length;System.out.println("\"" + s + "\"");System.out.print(" ");for (int i = 0; i < length; i++) {System.out.print(ulchar);}System.out.println("");}public Product createClone() {Product p = null;try {p = (Product) clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return p;}
}
import framework.*;/*** decochar字段中保存的是像装饰方框那样的环绕着字符串的字符。use方法会使用decochar字段中保存的字符把要显示的字符串框起来。* 例如,当decochar 中保存的字符为'*',use方法接收到的字符串为 Hello 的时候,显示结果如下* <p>* ******** *Hello** ********/
public class MessageBox implements Product {private char decochar;public MessageBox(char decochar) {this.decochar = decochar;}public void use(String s) {int length = s.getBytes().length;for (int i = 0; i < length + 4; i++) {System.out.print(decochar);}System.out.println("");System.out.println(decochar + " " + s + " " + decochar);for (int i = 0; i < length + 4; i++) {System.out.print(decochar);}System.out.println("");}public Product createClone() {Product p = null;try {p = (Product) clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return p;}
}

【主类】

import framework.*;public class Main {public static void main(String[] args) {// 准备Manager manager = new Manager();UnderlinePen upen = new UnderlinePen('~');MessageBox mbox = new MessageBox('*');MessageBox sbox = new MessageBox('/');manager.register("strong message", upen);manager.register("warning box", mbox);manager.register("slash box", sbox);// 生成Product p1 = manager.create("strong message");p1.use("Hello, world.");System.out.println();Product p2 = manager.create("warning box");p2.use("Hello, world.");System.out.println();Product p3 = manager.create("slash box");p3.use("Hello, world.");}
}
"Hello, world."~~~~~~~~~~~~~*****************
* Hello, world. *
*****************/
/ Hello, world. /
/Process finished with exit code 0

分析

对象种类繁多,无法将它们整合到一个类中时

在示例程序中,一共出现了如下3种样式。

  • 使用’~'为字符串添加下划线
  • 使用’*'为字符串添加边框
  • 使用’/'为字符串添加边框

本例比较简单,只生成了3种样式,不过无论多少种样式都可以生成。但是如果将每种样式都编写为一个类,类的数量将会非常庞大,源代码的管理也会变得非常困难。

想解耦框架与生成的实例时

在示例程序中,我们将复制(clone)实例的部分封装在framework包中了

在Manager类的create方法中,我们并没有使用类名,而是根据"strong message"和"slash box"等字符串(即实例名称)来生成相应的实例。与Java语言自带的生成实例的newSomething()方式相比,这种方式具有更好的通用性,且将框架从类名的束缚中解脱出来了

登场角色

在这里插入图片描述

  • Prototype(原型):定义用于复制现有实例来生成新实例的方法,如上面的Product
  • ConcretePrototype(具体的原型):负责实现复制现有实例并生成新实例的方法,如上面的MessageBox和UnderlinePen
  • Client(使用者):负责使用复制实例的方法生成新的实例,如上面的Manager

Spring源码分析

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:util="http://www.springframework.org/schema/util"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"><!-- 这里我们的 scope="prototype" 即 原型模式来创建 --><bean id="id01" class="com.atguigu.spring.bean.Monster" scope="prototype"/></beans>

scope=“prototype”:使用原型模式

package com.atguigu.spring.bean;/*** 注释* @author Administrator**/
public class Monster {private Integer id = 10 ;private String nickname = "牛魔王";private String skill = "芭蕉扇";public Monster() {System.out.println("monster 创建..");}public Monster(Integer id, String nickname, String skill) {//System.out.println("Integer id, String nickname, String skill被调用");this.id = id;this.nickname = nickname;this.skill = skill;}public Monster( String nickname, String skill,Integer id) {this.id = id;this.nickname = nickname;this.skill = skill;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getNickname() {return nickname;}public void setNickname(String nickname) {this.nickname = nickname;}public String getSkill() {return skill;}public void setSkill(String skill) {this.skill = skill;}@Overridepublic String toString() {return "Monster [id=" + id + ", nickname=" + nickname + ", skill="+ skill + "]";}}
package com.atguigu.spring.test;import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class ProtoType {public static void main(String[] args) {// TODO Auto-generated method stubApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");// 获取monster[通过id获取monster]Object bean = applicationContext.getBean("id01");System.out.println("bean" + bean); // 输出 "牛魔王" .....Object bean2 = applicationContext.getBean("id01");System.out.println("bean2" + bean2); //输出 "牛魔王" .....System.out.println(bean == bean2); // false,并不是同一个对象,只是属性相同}}

【运行】

monster 创建..
beanMonster [id=10, nickname=牛魔王, skill=芭蕉扇]
monster 创建..
bean2Monster [id=10, nickname=牛魔王, skill=芭蕉扇]
falseProcess finished with exit code 0

在这里插入图片描述

浅拷贝和深拷贝

浅拷贝

  • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象
  • 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值 (内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
  • sheep = (Sheep) super.clone()这种方式就是浅拷贝

深拷贝

  • 复制对象的所有基本数据类型的成员变量值
  • 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象

【实现方式】

  • 重写clone方法来实现深拷贝(缺点:麻烦,每个子对象都需要调用克隆方法重新赋值)
  • 通过对象序列化实现深拷贝(缺点:速度慢,如果调用拷贝的次数多,不要使用这种方式)

深拷贝工具类

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;public class DeepCloneUtil {public static Object deepClone(Object srcObject){ByteArrayOutputStream bos = null;ObjectOutputStream oos = null;ByteArrayInputStream bis = null;ObjectInputStream ois = null;Object result = null;try {bos = new ByteArrayOutputStream();oos = new ObjectOutputStream(bos);oos.writeObject(srcObject);bis = new ByteArrayInputStream(bos.toByteArray());ois = new ObjectInputStream(bis);result = ois.readObject();} catch (Exception e) {e.printStackTrace();} finally {try {bos.close();oos.close();bis.close();ois.close();} catch (IOException e) {e.printStackTrace();}}return result;}}

使用方式

A a = new A();
A a1 = (A)DeepCloneUtil.deepClone(a);

clone方法与Clonable接口

  • Object中定义了clone()方法,如果类实现了Cloneable接口,就可以调用clone()来进行实例复制;否则调用就会报CloneNotSupportedException异常
  • Cloneable接口中并没有声明任何方法。它只是被用来标记“可以使用clone方法进行复制”的,称为标记接口(marker interface)
  • clone 方法只会进行复制,并不会调用被复制实例的构造函数。此外,对于在生成实例时需要进行特殊的初始化处理的类,需要自已去实现 clone 方法,在其内部进行这些初始化处理

在这里插入图片描述

文章说明

  • 本文章为本人学习尚硅谷的学习笔记,文章中大部分内容来源于尚硅谷视频(点击学习尚硅谷相关课程),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对尚硅谷的优质课程表示感谢。
  • 本人还同步阅读《图解设计模式》书籍(图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社,2017.1),进而综合两者的内容,让知识点更加全面

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

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

相关文章

naive-ui的dialog.warning 关闭和阻止关闭

序&#xff1a; 1、如果你卡到 了&#xff0c;博主没写博客&#xff0c;可以在博主的公众号&#xff1a;“程序员野区” 留言。博主看到有时间再帮你去试 2、博主主要讲的怎么 主动关闭dialog和阻止dialog 自动关闭。 注意&#xff01;&#xff01;&#xff01;&#xff01;来&…

Spring Data Redis操作Redis

在Spring Boot项目中&#xff0c;可以使用Spring Data Redis来简化Redis操作&#xff0c;maven的依赖坐标&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></…

5分钟搞懂池化的本质

大家好啊&#xff0c;我是董董灿&#xff01; 在很多与计算机视觉相关的神经网络中&#xff0c;我们往往都会看到池化这一算法&#xff0c;它一般跟在卷积层后面。 神经网络中用到最多的池化方式无外乎是最大池化和平均池化。两者运算接近&#xff0c;区别在于是在kernel范围…

在使用《快递批量查询高手》时从TXT文本导入的快递单号出现乱码如何解决。

在日常 工作中&#xff0c;有没有单号用TXT 文档保存吗&#xff1f;那么没有出现这种情况呢&#xff0c;打开TXT文档进去看都是正常没有问题&#xff0c;一但导入软件中就出现乱码的&#xff1f;遇到这个种情况需要什么解决呢&#xff1f;小编今天就教 您一招解决好方法&#x…

电脑微信空间占用简便清理

1、打开电脑版微信、点击左下角的三根横线 2、点击左侧的“设置” 3、弹出层左侧点击“通用设置”->“存储空间管理” 4、点击清理缓存&#xff0c;或者管理 5、点击“管理”后&#xff0c;根据选择的筛选条件&#xff0c;勾线需要清理的&#xff0c;最后点击清理

WormGPT – 网络犯罪分子用来犯罪的人工智能工具

WormGPT – 网络犯罪分子用来发起商业电子邮件泄露攻击的生成式人工智能工具 前言 什么是蠕虫GPT&#xff08;WormGPT&#xff09; WormGPT是基于EleutherAI于2021年创建的大型语言模型GPT-J的AI模型。它具有无限的字符支持、聊天记忆保留和代码格式化功能。 如果未部署适当…

C#|无法打开cs文件设计窗口

报错信息&#xff1a;To prevent possible data loss before loading the designer, the following errors must be resolved: 解决方案&#xff1a;实不相瞒我把项目解决方案名称改短了就可以了。。有其他原因或者解决方案望不吝赐教。。

electron dialog.showMessageBox使用案例

electron 版本&#xff1a;25.3.1 index.html <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>Hello World!</title><meta http-equiv"Content-Security-Policy" content"script-src self unsa…

计算机视觉(三)未有深度学习之前

文章目录 图像分割基于阈值、基于边缘基于区域、基于图论 人脸检测Haar-like特征级联分类器 行人检测HOGSVMDPM 图像分割 把图像划分成若干互不相交的区域。经典的数字图像分割算法一般是基于灰度值的两个基本特征之一&#xff1a;不连续性和相似性。 基于阈值、基于边缘 基于…

《论文阅读》具有特殊Token和轮级注意力的层级对话理解 ICLR 2023

《论文阅读》具有特殊Token和轮级注意力的层级对话理解 前言简介问题定义模型构建知识点Intra-turn ModelingInter-turn Modeling分类前言 你是否也对于理解论文存在困惑? 你是否也像我之前搜索论文解读,得到只是中文翻译的解读后感到失望? 小白如何从零读懂论文?和我一…

MySQL事务

目录 前言 1.为什么存在事务 2.什么是事务 3.事务的版本支持 4.事务提交方式 5.事务常见操作方式 6.事务隔离级别 6.1如何理解隔离性 6.2隔离级别 6.3隔离性的查看与设置 6.4读未提交 6.5读提交 6.6可重复读 6.7串行化 7.多版本并发控制 7.1 3个记录隐藏列字段…

面向对象编程:从创建类到封装与构造方法的探索

1. 代码如何创建类&#xff1f; 在面向对象编程中&#xff0c;类是对一类事物的抽象&#xff0c;包含了静态的属性&#xff08;成员变量&#xff09;和动态的行为&#xff08;成员方法&#xff09;。在Java中&#xff0c;创建类的格式如下&#xff1a; 修饰词 class 类名 {//…

办公楼管理高手:一起来学烟雾监测实用技能!

在现代社会中&#xff0c;安全意识和防患意识越来越受到重视。特别是在大型办公楼等人员密集的场所&#xff0c;火灾的风险不容忽视。 为了保障员工和资产的安全&#xff0c;烟感监控成为一项至关重要的安全措施。烟感监控系统作为火灾预警的关键组成部分&#xff0c;能够及早发…

大数据Flink(四十九):框架版本介绍和编程语言选择

文章目录 框架版本介绍和编程语言选择 一、框架版本介绍 二、编程语言选择 框架版本介绍和编程语言选择

pandas学习

(个人学习使用) 添加索引 # index是行索引&#xff0c;columns是列索引 pd.DataFrame(score, indexidx, columnscol) 常用属性和方法 data.shape # 形状 data.index # 行索引 data.columns # 列索引 data.values # 里面的值&#xff0c;结果是ndarray类型数组 …

微信存储空间清理

1、打开电脑版微信、点击左下角的三根横线 2、点击左侧的“设置” 3、弹出层左侧点击“文件管理” 4、点击右下角“打开文件夹” 5、默认4打开的为当前登录账户对应的文件夹&#xff08;建议清理路径步骤7&#xff09; 6、点击“WeChat Files”查看其他账户的文件夹&#xff0c…

智能制造RFID设备包括哪些?

智能制造是现代制造业的重要发展方向&#xff0c;其核心是数字化、网络化和智能化。而在智能制造中&#xff0c;RFID设备是一种不可或缺的技术手段&#xff0c;主要用于实现物品的识别、追踪和化管理。以下是智能制造中常用的RFID设备及其功能&#xff1a; 1、 RFID读写器 RFID…

[JAVAee]synchronized关键字

目录 1.synchronized的特性 ①互斥性 ②可重入性 2.synchronized的使用示例 ①修饰普通方法 ②修饰静态方法 ③修饰代码块 1.synchronized的特性 ①互斥性 互斥性,就像是给门上锁了一样. 当A线程使用了被synchronized修饰的代码块并对其上锁,其他线程(B线程,C线程)想要使…

Hadoop 之 Centos 7 搭建 Zookeeper 3.8.2 集群(六)

Zookeeper 集群搭建 一.Centos 虚拟机配置1.新建虚拟机&#xff0c;选择【典型】2.下一步&#xff0c;【稍后安装操作系统】3.下一步&#xff0c;选择【Linux】【Centos7 64位】4.下一步&#xff0c;设置虚拟机名称和安装目录5.下一步&#xff0c;默认6.下一步&#xff0c;【自…

计算机系统结构-多处理机

概念&#xff0c;多处理机指的是&#xff0c;多台含cpu的机器共享一个存储器。 &#xff08;可以通过网络宽带&#xff0c;也可以通过线直连这个存储器。当然他们也可以有自己的私有存储器或者高速缓存&#xff09; 几个cpu公用一个总线&#xff0c;没问题。但是如果十几个cpu…