【设计模式——学习笔记】23种设计模式——备忘录模式Memento(原理讲解+应用场景介绍+案例介绍+Java代码实现)
案例引入
游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力),当大战Boss后攻击力和防御力下降,可以从备忘录对象恢复到大战前的状态
传统设计方案
针对每一种角色,设计一个类来存储该角色的状态
【分析】
- 一个对象,就对应一个保存对象状态的对象, 这样当我们游戏的对象很多时,不利于管理,开销也很大
- 传统的方式是简单地做备份,new出另外一个对象出来,再把需要备份的数据放到这个新对象,但是这样暴露了对象内部的细节
- 优化方式:使用备忘录模式
介绍
基本介绍
- 备忘录模式在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
- 可以这样理解备忘录模式: 现实生活中的备忘录是用来记录某些要去做的事情或者是记录已经达成的共同意见的事情,以防忘记。而在软件层面,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作
- 备忘录模式属于行为型模式
登场角色
Originator(生成者)
:Originator角色会在保存自己的最新状态时生成Memento角色。当把以前保存的Memento角色传递给Originator角色时,它会将自已恢复至生成该Memento角色时的状态Memento(备忘录)
:Memento角色会将Originator 角色的内部信息整合在一起。在 Memento 角色中虽然保存了Originator 角色的信息,但它不会向外部公开这些信息(通过对方法的权限字符进行设置)Caretaker(负责人)
:当Caretaker角色
想要保存当前的Originator角色
的状态时,会通知Originator角色
。Originator角色
在接收到通知后会生成Memento角色
的实例并将其返回给Caretaker角色
。由于以后可能会用Memento实例
来将Originator
恢复至原来的状态,因此Caretaker角色
会一直保存Memento实例
在示例程序中。Caretaker角色
只能使用Memento角色
的两种接口(API)中的窄接口,也就是说它无法访问Memento角色
内部的所有信息(比如案例三中Caretaker角色
只能获取Memento角色
的金钱信息,无法进行创建Memento实例
等操作)。它只是将Originator角色
生成的Memento角色
当作一个黑盒子保存起来。虽然Originator角色
和Memento角色
之间是强关联关系,但Caretaker角色
和Memento角色
之间是弱关联关系。Memento角色
对Caretaker角色
隐藏了自身的内部信息
【划分Caretaker角色
和Originator角色
的意义】
Caretaker角色的职责是决定何时拍摄快照,何时撤销以及保存Memento角色。另一方面,Originator 角色的职责则是生成Memento角色和使用接收到的Memento角色来恢复自己的状态。有了这样的职责分担,当我们需要应对以下的需求变更时,就不用修改Originator角色
- 变更为可以多次撤销
- 变更为不仅可以撤销,还可以将现在的状态保存在文件中
【Caretaker角色只能通过窄接口(API)来操作 Memento角色,如果Caretaker角色可以随意地操作Memento角色,会发生什么问题呢】
- 破坏对象的封装性:Memento角色负责存储Originator角色的状态数据,如果Caretaker角色可以随意地操作Memento角色,那么就破坏了Memento对象和Originator对象的封装性
- 不安全的状态恢复:如果Caretaker角色可以随意地操作Memento角色,可能会改变Memento对象中存储的状态数据,导致状态恢复产生错误
- 无法保证数据的完整性:如果Caretaker角色可以随意地操作Memento角色,可能会在不正确的时间点保存状态数据,导致数据的不完整性
案例实现
案例一(基础案例)
类图
Originator
:需要被保存状态的对象Memento
:备忘录对象,负责记录Originator对象的状态Caretaker
:守护者对象,负责保存多个备忘录对象,一般使用集合进行管理,提高管理效率
实现
【Originator】
package com.atguigu.memento.theory;public class Originator {/*** 角色的状态信息*/private String state;public String getState() {return state;}public void setState(String state) {this.state = state;}/*** 编写一个方法,可以保存一个状态对象 Memento* 因此编写一个方法,返回 Memento** @return*/public Memento saveStateMemento() {return new Memento(state);}/*** 通过备忘录对象获取原有状态信息,恢复状态** @param memento*/public void getStateFromMemento(Memento memento) {state = memento.getState();}
}
【Memento】
package com.atguigu.memento.theory;public class Memento {/*** 用来保存状态信息*/private String state;/*** 构造器** @param state*/public Memento(String state) {super();this.state = state;}/*** 获取保存的状态信息* @return*/public String getState() {return state;}}
【Caretaker】
package com.atguigu.memento.theory;import java.util.ArrayList;
import java.util.List;/*** 统一管理备忘录对象*/
public class Caretaker {/*** 在 List 集合中会有很多的备忘录对象* 如果想要保存多个Originator的多个状态,可以使用HashMap<Originator的ID,List<Memento>>*/private List<Memento> mementoList = new ArrayList<Memento>();public void add(Memento memento) {mementoList.add(memento);}/*** 获取 Originator 的 第 index 个 备忘录对象(即所保存的状态)** @param index* @return*/public Memento get(int index) {return mementoList.get(index);}
}
【主类】
package com.atguigu.memento.theory;public class Client {public static void main(String[] args) {Originator originator = new Originator();// 备忘录对象管理器Caretaker caretaker = new Caretaker();originator.setState(" 状态#1 攻击力 100 ");//保存了当前的状态caretaker.add(originator.saveStateMemento());originator.setState(" 状态#2 攻击力 80 ");caretaker.add(originator.saveStateMemento());originator.setState(" 状态#3 攻击力 50 ");caretaker.add(originator.saveStateMemento());System.out.println("当前的状态是 =" + originator.getState());//希望得到状态 1, 将 originator 恢复到状态1originator.getStateFromMemento(caretaker.get(0));System.out.println("恢复到状态1 , 当前的状态是 =" + originator.getState());}}
【运行】
当前的状态是 = 状态#3 攻击力 50
恢复到状态1 , 当前的状态是 = 状态#1 攻击力 100 Process finished with exit code 0
案例二
类图
实现
【Originator:GameRole】
package com.atguigu.memento.game;public class GameRole {private int vit;private int def;/*** 创建Memento,即根据当前的状态得到Memento** @return*/public Memento createMemento() {return new Memento(vit, def);}/*** 从备忘录对象,恢复GameRole的状态** @param memento*/public void recoverGameRoleFromMemento(Memento memento) {this.vit = memento.getVit();this.def = memento.getDef();}/*** 显示当前游戏角色的状态*/public void display() {System.out.println("游戏角色当前的攻击力:" + this.vit + " 防御力: " + this.def);}public int getVit() {return vit;}public void setVit(int vit) {this.vit = vit;}public int getDef() {return def;}public void setDef(int def) {this.def = def;}}
【Memento】
package com.atguigu.memento.game;public class Memento {/*** 攻击力*/private int vit;/*** 防御力*/private int def;public Memento(int vit, int def) {this.vit = vit;this.def = def;}public int getVit() {return vit;}public void setVit(int vit) {this.vit = vit;}public int getDef() {return def;}public void setDef(int def) {this.def = def;}}
【Caretaker】
package com.atguigu.memento.game;/*** 守护者对象, 保存游戏角色的状态*/
public class Caretaker {/*** 因为大战Boss之前只有一个状态,所以这里保存一次状态,不需要使用集合*/private Memento memento;//对GameRole保存多次状态//private ArrayList<Memento> mementos;//对多个GameRole保存多个状态//private HashMap<String, ArrayList<Memento>> rolesMementos;public Memento getMemento() {return memento;}public void setMemento(Memento memento) {this.memento = memento;}}
【主类】
package com.atguigu.memento.game;public class Client {public static void main(String[] args) {//创建游戏角色GameRole gameRole = new GameRole();gameRole.setVit(100);gameRole.setDef(100);System.out.println("和boss大战前的状态");gameRole.display();//把当前状态保存caretakerCaretaker caretaker = new Caretaker();caretaker.setMemento(gameRole.createMemento());System.out.println("和boss大战~~~");gameRole.setDef(30);gameRole.setVit(30);gameRole.display();System.out.println("大战后,使用备忘录对象恢复到站前");gameRole.recoverGameRoleFromMemento(caretaker.getMemento());System.out.println("恢复后的状态");gameRole.display();}}
【运行】
和boss大战前的状态
游戏角色当前的攻击力:100 防御力: 100
和boss大战~~~
游戏角色当前的攻击力:30 防御力: 30
大战后,使用备忘录对象恢复到站前
恢复后的状态
游戏角色当前的攻击力:100 防御力: 100Process finished with exit code 0
案例三
案例说明
- 游戏是自动进行的
- 游戏的主人公通过掷骰子来决定下一个状态
- 当骰子点数为1的时候,主人公的金钱会增加
- 当骰子点数为2的时候,主人公的金钱会减少
- 当骰子点数为6的时候,主人公会得到水果
- 主人公没有钱时游戏就会结束
类图
实现
【Originator:Gamer】
package com.atguigu.memento.Sample.game;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;public class Gamer {/*** 所持金钱*/private int money;/*** 获得的水果*/private List fruits = new ArrayList();/*** 随机数生成器*/private Random random = new Random();/*** 表示水果种类的数组*/private static String[] fruitsName = {"苹果", "葡萄", "香蕉", "橘子",};/*** 构造函数** @param money*/public Gamer(int money) {this.money = money;}/*** 获取当前所持金钱** @return*/public int getMoney() {return money;}/*** 投掷骰子进行游戏*/public void bet() {// 掷骰子,随机获取一个点数int dice = random.nextInt(6) + 1;if (dice == 1) {// 骰子结果为1…增加所持金钱money += 100;System.out.println("所持金钱增加了。");} else if (dice == 2) {// 骰子结果为2…所持金钱减半money /= 2;System.out.println("所持金钱减半了。");} else if (dice == 6) {// 骰子结果为6…获得水果String f = getFruit();System.out.println("获得了水果(" + f + ")。");fruits.add(f);} else {// 骰子结果为3、4、5则什么都不会发生System.out.println("什么都没有发生。");}}/*** 拍摄快照** @return*/public Memento createMemento() {Memento m = new Memento(money);Iterator it = fruits.iterator();while (it.hasNext()) {String f = (String) it.next();if (f.startsWith("好吃的")) {// 只保存好吃的水果m.addFruit(f);}}return m;}/*** 撤销到指定状态** @param memento*/public void restoreMemento(Memento memento) {this.money = memento.money;this.fruits = memento.getFruits();}/*** 用字符串输出主人公的状态** @return*/public String toString() {return "[money = " + money + ", fruits = " + fruits + "]";}/*** 随机获取一个水果** @return*/private String getFruit() {String prefix = "";if (random.nextBoolean()) {prefix = "好吃的";}return prefix + fruitsName[random.nextInt(fruitsName.length)];}
}
【Memento】
package com.atguigu.memento.Sample.game;import java.util.ArrayList;
import java.util.List;public class Memento {/*** 游戏主人公所持金钱*/int money;/*** 游戏主人公当前获得的水果*/ArrayList fruits;/*** 获取当前所持金钱(narrow interface)* narrow interface:Memento角色为外部的 Caretaker 角色提供了“窄接口(API)”。可以通过窄接口(API)获取的Memento角色的内部信息非常有限,因此可以有效地防止信息泄露* @return*/public int getMoney() {return money;}/*** 构造函数(wide interface),只有相同包的Gamer类才使用该构造方法,因为方法修饰类型是default** @param money*/Memento(int money) {this.money = money;this.fruits = new ArrayList();}/*** 添加水果(wide interface)** @param fruit*/void addFruit(String fruit) {fruits.add(fruit);}/*** 获取当前所持所有水果(wide interface)* wide interface:“宽接口(API)”是指所有用于获取恢复对象状态信息的方法的集合。由于宽接口(API)会暴露所有Memento角色的内部信息,因此能够使用宽接口(API)的只有Originator 角色* @return*/List getFruits() {return (List) fruits.clone();}
}
【Caretaker】
package com.atguigu.memento.Sample;import com.atguigu.memento.Sample.game.Gamer;
import com.atguigu.memento.Sample.game.Memento;public class Main {public static void main(String[] args) {// 最初的所持金钱数为100Gamer gamer = new Gamer(100);// 保存最初的状态Memento memento = gamer.createMemento();for (int i = 0; i < 10; i++) {// 显示掷骰子的次数System.out.println("==== " + i);// 显示主人公现在的状态System.out.println("当前状态:" + gamer);// 进行游戏gamer.bet();System.out.println("所持金钱为" + gamer.getMoney() + "元。");// 决定如何处理Mementoif (gamer.getMoney() > memento.getMoney()) {System.out.println(" (所持金钱增加了许多,因此保存游戏当前的状态)");memento = gamer.createMemento();} else if (gamer.getMoney() < memento.getMoney() / 2) {System.out.println(" (所持金钱减少了许多,因此将游戏恢复至以前的状态)");gamer.restoreMemento(memento);}// 等待一段时间try {Thread.sleep(1000);} catch (InterruptedException e) {}System.out.println("");}}
}
【运行】
==== 0
当前状态:[money = 100, fruits = []]
什么都没有发生。
所持金钱为100元。==== 1
当前状态:[money = 100, fruits = []]
所持金钱增加了。
所持金钱为200元。(所持金钱增加了许多,因此保存游戏当前的状态)==== 2
当前状态:[money = 200, fruits = []]
什么都没有发生。
所持金钱为200元。==== 3
当前状态:[money = 200, fruits = []]
什么都没有发生。
所持金钱为200元。==== 4
当前状态:[money = 200, fruits = []]
什么都没有发生。
所持金钱为200元。==== 5
当前状态:[money = 200, fruits = []]
什么都没有发生。
所持金钱为200元。==== 6
当前状态:[money = 200, fruits = []]
什么都没有发生。
所持金钱为200元。==== 7
当前状态:[money = 200, fruits = []]
什么都没有发生。
所持金钱为200元。==== 8
当前状态:[money = 200, fruits = []]
所持金钱减半了。
所持金钱为100元。==== 9
当前状态:[money = 100, fruits = []]
所持金钱增加了。
所持金钱为200元。Process finished with exit code 0
拓展
使用序列化(Serialization)功能来将Memento类的实例保存为文件。请修改示例程序以实现下列功能。
- 在应用程序启动时,如果发现不存在
game.dat
文件时,以所持金钱数目为100开始游戏;如果发现 game.dat 已经存在,则以文件中所保存的状态开始游戏 - 当所持金钱大量增加后,将Memento类的实例保存到
game.dat
文件中
- 将Memento继承Serializable接口来实现序列化操作,除此之外,不需要进行其他修改
public class Memento implements Serializable {}
- 想要扩展实例保存到文件的功能,只需要修改
Caretaker
类
package com.atguigu.memento.A4;import com.atguigu.memento.A4.game.Gamer;
import com.atguigu.memento.A4.game.Memento;import java.io.*;public class Main {/*** 数据保存的文件名*/public static final String SAVE_FILE_NAME = "game.dat";public static void main(String[] args) {// 最初的所持金钱数为100Gamer gamer = new Gamer(100);// 从文件中读取起始状态Memento memento = loadMemento();if (memento != null) {System.out.println("读取上次保存存档开始游戏。");gamer.restoreMemento(memento);} else {System.out.println("新游戏。");memento = gamer.createMemento();}for (int i = 0; i < 100; i++) {// 显示次数System.out.println("==== " + i);// 显示当前主人公的状态System.out.println("当前状态:" + gamer);// 进行游戏gamer.bet(); System.out.println("所持金钱为" + gamer.getMoney() + "元。");// 决定如何处理Mementoif (gamer.getMoney() > memento.getMoney()) {System.out.println(" (所持金钱增加了许多,因此保存游戏当前的状态)");memento = gamer.createMemento();// 将实例保存至文件中saveMemento(memento);} else if (gamer.getMoney() < memento.getMoney() / 2) {System.out.println(" (所持金钱减少了许多,因此将游戏恢复至以前的状态)");gamer.restoreMemento(memento);}// 等待一段时间try {Thread.sleep(1000);} catch (InterruptedException e) {}System.out.println("");}}/*** 保存实例到文件中* @param memento*/public static void saveMemento(Memento memento) { try {ObjectOutput out = new ObjectOutputStream(new FileOutputStream(SAVE_FILE_NAME));out.writeObject(memento);out.close();} catch (IOException e) {e.printStackTrace();}}/*** 从文件中读取实例* @return*/public static Memento loadMemento() { Memento memento = null;try {ObjectInput in = new ObjectInputStream(new FileInputStream(SAVE_FILE_NAME));memento = (Memento)in.readObject();in.close();} catch (FileNotFoundException e) {System.out.println(e.toString());} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return memento;}
}
总结
【优点】
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回退到某个历史的状态
- 实现了信息的封装,使用户不需要关心状态的保存细节
【缺点】
- 如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存,这个需要注意。为了节约内存,备忘录模式可以和原型模式配合使用
【适用应用场景】
- 打游戏时的存档
- Windows 里的
ctrl+z
- IE 中的后退
- 数据库的事务管理
文章说明
- 本文章为本人学习尚硅谷的学习笔记,文章中大部分内容来源于尚硅谷视频(点击学习尚硅谷相关课程),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对尚硅谷的优质课程表示感谢。
- 本人还同步阅读《图解设计模式》书籍(图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社,2017.1),进而综合两者的内容,让知识点更加全面
相关文章:

【设计模式——学习笔记】23种设计模式——备忘录模式Memento(原理讲解+应用场景介绍+案例介绍+Java代码实现)
案例引入 游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力),当大战Boss后攻击力和防御力下降,可以从备忘录对象恢复到大战前的状态 传统设计方案 针对每一种角色,设计一个类来存储该角色的状态 【分析】…...

致谢丨感谢有你,JumpServer开源项目九周年致谢名单
2014年到2023年,JumpServer开源项目已经走过了九年的时间。感谢以下社区贡献者对JumpServer项目的帮助和支持。 因为有你,一切才能成真。 JumpServer开源项目贡献者奖杯将于近日邮寄到以上贡献者手中,同时JumpServer开源项目组还准备了一份小…...

使用 Python 和 Flask 构建简单的 Restful API 第 1 部分
一、说明 我将把这个系列分成 3 或 4 篇文章。在本系列的最后,您将了解使用flask构建 restful API 是多么容易。在本文中,我们将设置环境并创建将显示“Hello World”的终结点。 我假设你的电脑上安装了python 2.7和pip。我已经在python 2.7上测试了本文…...
【深度学习所有损失函数】在 NumPy、TensorFlow 和 PyTorch 中实现(2/2)
一、说明 在本文中,讨论了深度学习中使用的所有常见损失函数,并在NumPy,PyTorch和TensorFlow中实现了它们。 (二-五)见 六、稀疏分类交叉熵损失 稀疏分类交叉熵损失类似于分类交叉熵损失,但在真实标签作为整数而不是独热编码提…...

Hazel 引擎学习笔记
目录 Hazel 引擎学习笔记学习方法思考引擎结构创建工程程序入口点日志系统Premake\MD没有 cpp 文件的项目会出错include 到某个库就要包含这个库的路径,注意头文件展开 事件系统 获取和利用派生类信息预编译头文件抽象窗口类和 GLFWgit submodule addpremake 脚本禁…...

Linux系统下Redis3.2集群
本节主要学习reids主从复制的概念,作用,缺点,流程,搭建,验证,reids哨兵模式的概念,作用,缺点,结构,搭建,验证等。 文章目录 一、redis主从复制 …...

Android图形-合成与显示-SurfaceTestDemo
目录 引言: 主程序代码: 结果呈现: 小结: 引言: 通过一个最简单的测试程序直观Android系统的native层Surface的渲染显示过程。 主程序代码: #include <cutils/memory.h> #include <utils/L…...

高压放大器怎么设计(高压放大器设计方案)
高压放大器是一种用于将低电压信号转换成高电压信号的电子设备,广泛应用于通信、雷达、医疗设备等领域。在设计高压放大器时,需要考虑多种因素,如输入输出信号的特性、电路结构的选择、电源和负载匹配等。本文将介绍高压放大器的设计方法和注…...
SpringBoot yml配置注入
yaml语法学习 1、配置文件 SpringBoot使用一个全局的配置文件 , 配置文件名称是固定的 application.properties 语法结构 :keyvalue application.yml 语法结构 :key:空格 value 配置文件的作用:修改SpringBoot自动…...

中科亿海微乘法器(LPMMULT)
引言 FPGA(可编程逻辑门阵列)是一种可在硬件级别上重新配置的集成电路。它具有灵活性和可重构性,使其成为处理各种应用的理想选择,包括数字信号处理、图像处理、通信、嵌入式系统等。在FPGA中,乘法器是一种重要的硬件资…...

Redis_持久化(AOF、RDB)
6. Redis AOF 6.1 简介 目前,redis的持久化主要应用AOF(Append Only File)和RDF两大机制,AOF以日志的形式来记录每个写操作(增量保存),将redis执行过的所有指令全部安全记录下来(读…...

开源数据库Mysql_DBA运维实战 (部署服务篇)
前言❀ 1.数据库能做什么 2.数据库的由来 数据库的系统结构❀ 1.数据库系统DBS 2.SQL语言(结构化查询语言) 3.数据访问技术 部署Mysql❀ 1.通过rpm安装部署Mysql 2.通过源码包安装部署Mysql 前言❀ 1.数据库能做什么 a.不论是淘宝,吃鸡,爱奇艺…...

【Java学习】System.Console使用
背景 在自学《Java核心技术卷1》的过程中看到了对System.Console的介绍,编写下列测试代码, public class ConsoleTest {public static void main(String[] args) {Console cs System.console();String name cs.readLine("AccountInfo: ");…...
从零学算法154
154.已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums [0,1,4,4,5,6,7] 在变化后可能得到: 若旋转 4 次,则可以得到 [4,5,6,7,0,1,4] 若旋转 7 次&#…...
95 | Python 设计模式 —— 策略模式
策略模式(Strategy Pattern) 引言 策略模式是一种行为型设计模式,它定义了一系列的算法,并将每个算法封装在独立的策略类中,使得这些算法可以相互替换,而不影响客户端的使用。策略模式可以让客户端根据不同的需求选择不同的算法,从而使得系统更加灵活和可扩展。 在本…...
【BASH】回顾与知识点梳理(十九)
【BASH】回顾与知识点梳理 十九 十九. 循环 (loop)19.1 while do done, until do done (不定循环)19.2 for...do...done (固定循环)19.3 for...do...done 的数值处理(C写法)19.4 搭配随机数与数组的实验19.5 shell script 的追踪与 debug19.6 what_to_eat-2.sh debug结果解析 该…...

Selenium之css怎么实现元素定位?
世界上最远的距离大概就是明明看到一个页面元素站在那里,但是我却定位不到!! Selenium定位元素的方法有很多种,像是通过id、name、class_name、tag_name、link_text等等,但是这些方法局限性太大, 随着自动…...

计算机基础之RAID技术
概述 RAID,Redundant Array of Independent Disks,独立磁盘冗余阵列,一种把多块独立的硬盘(物理硬盘)按不同的方式组合起来形成一个硬盘组(逻辑硬盘),从而提供比单个硬盘更高的存储…...

辽宁线上3D三维虚拟工厂生产仿真系统应用场景及优势
工厂虚拟仿真是一种基于计算机技术和虚拟现实技术的数字化解决方案,它可以通过模拟工厂中的设备、流程和操作,来为工程师和操作人员提供了一个沉浸式的虚拟环境,帮助他们更好地了解和优化工厂生产过程。 工厂VR三维可视化技术为工业生产提供了…...
csrf跨站请求的相关装饰器、Auth模块(模块的使用、相关方法、退出系统、修改密码功能、注册功能)、扩展默认的auth_user表
一、csrf跨站请求的相关装饰器 django.middleware.csrf.CsrfViewMiddlewareDjango中有一个中间件对csrf跨站做了验证,我只要把csrf的这个中间件打开, 那就意味着所有的方法都要被验证 在所有的视图函数中:只有几个视图函数做验证只有几个函数…...
(WWW2023)论文阅读-Detecting Social Media Manipulation in Low-ResourceLanguages
论文链接:https://arxiv.org/pdf/2011.05367.pdf 摘要 社交媒体被故意用于恶意目的,包括政治操纵和虚假信息。大多数研究都集中在高资源语言上。然而,恶意行为者会跨国家/地区和语言共享内容,包括资源匮乏的语言。 在这里…...
centos-stream-9 centos9 配置国内yum源 阿里云源
源配置 tips: yum配置文件路径 /etc/yum.repos.d/centos.repo 1.备份源配置 [Very Important!]mv /etc/yum.repos.d/centos.repo /etc/yum.repos.d/centos.repo.backup2.Clean Cache: yum clean all3.Backup the Old CentOS-Base.repo If exist this file.cd /etc/yum.repos.…...

查看单元测试用例覆盖率新姿势:IDEA 集成 JaCoCo
1、什么是 IDEA IDEA 全称 IntelliJ IDEA,是 Java 编程语言开发的集成环境。IntelliJ 在业界被公认为最好的 Java 开发工具,尤其在智能代码助手、代码自动提示、重构、JavaEE 支持、各类版本工具(git、SVN 等)、JUnit、CVS 整合、代码分析、 创新的 GUI…...
js和nodejs如何将文件切片和合并
nodejs进行文件切片合并 使用nodejs读取文件流,并对流进行切片合并等操作,就需要用到Buffer对象,可对文件流进行切片,并合并。 const fs require(fs)// 读取一个文件,使用fs读取文件获取一个Buffer类型数据 const b…...

Java内存模型
Java内存模型全称JMM(Java Memory Model) 内存主要有堆和栈组成 下面来一段demo代码详细讲解堆栈的作用,以及流程 public class Employee {private String name;private Integer age;private Department department;public Employee(){}pub…...

[国产MCU]-BL602开发实例-看门狗定时器(WDG)
看门狗定时器(WDG) 文章目录 看门狗定时器(WDG)1、看门狗定时器(WDG)介绍2、看门狗定时器驱动API介绍3、看门狗定时器使用实例看门狗(Watchdog),又叫看门狗定时器(Watchdog Timer),是一种硬件的计时设备,当系统的主程序发生某些错误时,导致未及时清除看门狗计时器…...

28 | Boss直聘数据分析
针对boss直聘网的招聘信息,然后分析互联网发展排名前十的城市在互联网方面职位的薪水,学历要求,经验要求,等等信息。 准备从以下几个方面进行分析: (1)各个城市的平均工资 (2)各个学历的平均工资 (3)各个岗位的平均工资 (4)不同工作经验要求的工资 (5)各个经验…...
Hash 缓存
Hash 缓存 输出文件名(Hash) 静态资源缓存是前端性能优化的一个点,所以在前端开发过程中,一般会最大限度的利用缓存(这里主要是强缓存)。如果设置了强缓存后,每次当我们部署了新的项目文件到线…...
腾讯云CVM服务器标准型S5性能CPU处理器测试
腾讯云服务器CVM标准型S5实例是次新一代的标准型实例,CPU采用主频2.5GHzIntel Xeon Cascade Lake或者Intel Xeon Cooper Lake处理器,睿频3.1GHz,云服务器S5基于全新优化虚拟化平台,提供了平衡、稳定的计算、内存和网络资源&#x…...
【左神算法刷题班】第16节:累加和为k的数组、逆序对问题、约瑟夫环问题
题目1 给定一个有正、有负、有0的数组arr, 给定一个整数k, 返回arr的子集是否能累加出k 1)正常怎么做? 2)如果arr中的数值很大,但是arr的长度不大,怎么做? 问题 1)…...