Spring Boot 事件机制 Event 入门

Spring Boot 事件机制 Event 入门 Spring 事件机制入门示例引入依赖PointEventPointEventPublisherPointEventListenerUserController Spring 内置事件ApplicationContextEventSpringApplicationEvent 参考 Spring 事件机制 JDK 也内置了事件机制的实现,考虑到通用性,Spring 的事件机制是基于它之上进行拓展。因此,ApplicationEvent 继承自 java.util.EventObject,ApplicationListener 继承自 java.util.EventListener。 Spring 基于观察者模式,实现了自身的事件机制,由三部分组成: 事件 ApplicationEvent:通过继承它,实现自定义事件。另外,通过它的 source 属性可以获取事件源,timestamp 属性可以获得发生时间。事件发布者 ApplicationEventPublisher:通过它,可以进行事件的发布。事件监听器 ApplicationListener:通过实现它,进行指定类型的事件的监听。 入门示例 引入依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.6</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>boot-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>boot-demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.

C++中static_cast和dynamic_cast强制类型转换

文章目录 1.static_cast关键字(编译时类型检查)2.dynamic_cast关键字(运行时类型检查)3. std::shared_ptr 智能指针强转 1.static_cast关键字(编译时类型检查) 用法: static_cast < type-id > ( expression ),该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性 它主要有如下几种用法: (1)用于基本数据类型之间的转换,如把int转换为char,把int转换成enum,但这种转换的安全性需要开发者自己保证(这可以理解为保证数据的精度,即程序员能不能保证自己想要的程序安全),如在把int转换为char时,如果char没有足够的比特位来存放int的值(int>127或int<-127时),那么static_cast所做的只是简单的截断,及简单地把int的低8位复制到char的8位中,并直接抛弃高位。(2)把空指针转换成目标类型的空指针(3)把任何类型的表达式类型转换成void类型(4)用于类层次结构中父类和子类之间指针和引用的转换。 对于static_cast,上行转换时安全的,而下行转换时不安全的 因为static_cast的转换时粗暴的,它仅根据类型转换语句中提供的信息(尖括号中的类型)来进行转换,这种转换方式对于上行转换,由于子类总是包含父类的所有数据成员和函数成员,因此从子类转换到父类的指针对象可以没有任何顾虑的访问其(指父类)的成员。 而对于下行转换为什么不安全,是因为static_cast只是在编译时进行类型检查,没有运行时的类型检查,具体原理在dynamic_cast中说明。 2.dynamic_cast关键字(运行时类型检查) 用法: 【格式】dynamic_cast<type_id>(expression) : 该运算符把expression转换为type_id 类型, type_id 可以为类的指针、类的引用、void*,expression为对应的指针或引用. dynamic_cast是四个强制类型转换操作符中最特殊的一个,它支持运行时识别指针或引用。首先,dynamic_cast依赖于RTTI信息,其次,在转换时,dynamic_cast会检查转换的source对象是否真的可以转换成target类型, 这种检查不是语法上的,而是真实情况的检查。若对指针进行dynamic_cast,失败返回nullptr,成功返回正常cast后的对象指针;若对引用进行dynamic_cast,失败抛出一个异常,成功返回正常cast后的对象引用 dynamic_cast,主要用于类层次间的转换: 上行转换(子类转父类),转换安全,成功返回类对象指针, 此时和static_cast 作用一样。 下行转换(父类转子类), 父类中要有虚函数,否则编译器报错。转换分几种情况: a. 父类指针指向子类对象,转换安全, dynamic_cast返回类对象指针。 b.父类指针指向父类对象,转换不安全,dynamic_cast 返回nullptr。 此时若使用static_cast, 返回非空指针,更不安全。 eg: #include <iostream> using namespace std; class A{ public: A(){} virtual void eat() { std::cout<<"A::eat()"<<std::endl; } void sing(){ std::cout<<"A::sing()"<<std::endl; } ~A(){}; }; class B : public A{ public: B() {}; void eat() { std::cout<<"

Es6模块化

总结 一、常用的模块化代码:CommonJS、ES6 二、CommonJS语法 2.1 暴露 暴漏任意数据 module.exports = 值;暴露多个数据 module.exports.键名 = 值;使用exports的方式暴露 exports.键名 = 值 2.1 导入 let test = require("文件路径") 文件:require('./文件名') require('../文件名') 省略文件后缀基本上是.js/.json为后缀的文件,除了这两个必须将后缀写清楚 文件中路径的./和…/是文件和文件之间在目录的层级上的路径,而非终端中的路径 文件夹:require('./文件夹名') require('../文件夹名') 需要在这个文件夹里面查找package.json里面main属性的文件名,默认是index.js 如果在这个目录中这个文件则执行运行; 如果没有,有其他名称的文件,路径将自动补全: require(‘./文件夹名/文件名’) require(‘…/文件夹名/文件名’) 模块名:内置模块、第三方模块 如果是第三方模块名在当前文件夹下的node_modules目录里面没有找到,则自动上上一个文件目录的node_modules查找 注意事项: 如果没有添加文件后缀,会默认按照.js/.json后缀的方式引入文件,同时有js和json同名的文件,没有添加文件后缀 js文件权重高其他文件后缀名,会按照.js方式载入如果是文件夹则会默认加载该文件夹下的package.json文件中main属性对应的文件如果是内置模块或者是npm安装的模块,直接使用包名字即可npm引入包时,如果当前文件夹下的node_modules没有,则会自动向上查找 三、ES6模块化语法 3.1 暴露 分别暴露:export 变量声明 export let 变量 = 值;统一暴露: export {变量1,变量2...}默认暴露: export default 值; 值的数据类型是所有的常用数据类型(number、string、boolean、array、object、funciton) 3.2 导入 适合于(分别暴露和统一暴露)的导入形式有两种: 通用形式的导入:import * as 别名 from '子模块文件路径' 这里的*表示所有,as表示别名 from来自于哪一个目标文件解构形式的导入:import {} from '子模块文件路径' {}里面写什么是子模块中export后面的变量名 适合于默认暴露的导入形式有两种: 通用形式的导入:import * as 别名 from '子模块文件路径'默认形式的导入:import 变量名 from '子模块文件路径' 注意:常见的错误写法 export default let a = 10;

多媒体技术与应用|图像平滑

图像平滑 实际获得的图像在形成、传输、接收和处理中,存在外部干扰和内部干扰,如: – 元器件灵敏度的不均匀性; – 量化噪声 – 传输过程中的误差 – 人为因素等 因此需要图像平滑处理。 图像平滑的目的: – 改善图像特征 – 抽出对象特征 空间平滑滤波增强 空域平滑滤波器常用的有邻域均值法和中值滤波法。 邻域平均法 假设图像由许多灰度恒定的小块组成,相邻像素间存在很高的空间相关性,而噪声则相对独立。可用邻域内各像素的灰度平均值代替该像素原来的灰度值,实现图像的平滑。 其作用相当于用这样的模板同图像卷积。 这种算法简单,但它的主要缺点是在降低噪声的同时使图像产生模糊,特别在边缘和细节处。而且邻域越大,在去噪能力增强的同时模糊程度越严重。 超限像素平滑法 这算法对抑制椒盐噪声比较有效,对保护仅有微小灰度差的细节及纹理也有效。可见随着邻域增大,去噪能力增强,但模糊程度也大。 同局部平滑法相比,超限像元平滑法去椒盐噪声效果更好。 灰度最接近的k个邻点平均法 在n×n的窗口内,属于同一集合体的像素,它们的灰度值将高度相关。因此,可用窗口内与中心像素的灰度最接近的K个邻像素的平均灰度来代替窗口中心像素的灰度值。 较小的K值使噪声方差下降较小,但保持细节效果较好;而较大的K值平滑噪声较好,但会使图像边缘模糊。实验证明,对于3×3的窗口,取K=6为宜。 最大均匀性平滑 为避免消除噪声引起边缘模糊,该算法先找出环绕图像中每像素的最均匀区域,然后用这区域的灰度均值代替该像素原来的灰度值。 有选择保边缘平滑法 该方法对图像上任一像素(x,y)的5×5邻域,采用9个掩模,其中包括一个3×3正方形、4个五边形和4个六边形。计算各个掩模的均值和方差,对方差进行排序,最小方差所对应的掩模区的灰度均值就是像素(x,y) 的输出值。 有选择保边缘平滑法既能够消除噪声,又不破坏区域边界的细节。 加权邻域平均 对于边界上无法通过模板操作的点,通常的做法是复制原图像素的灰度级。 BOX模板 高斯模板 掩模不同,中心点或邻域的重要程度也不相同,因此,应根据问题的需要选取合适的掩模。但不管什么样的掩模,必须保证全部权系数之和为单位值,这样可保证输出图像灰度值在许可范围内,不会产生“溢出”现象。 中值滤波 中值滤波是一种典型的低通滤波器; • 在一定条件下,可克服线性滤波所带来的图像细节模糊; • 对滤除脉冲干扰及图像扫描噪声非常有效; • 目的是保护图像边缘的同时去除噪声。 方法:将灰度级从小到大排列,取中间值。 中值滤波在抑制图象随机脉冲噪声方面甚为有效。且运算速度快,可硬化,便于实时处理。 中值滤波法能有效削弱椒盐噪声,且比邻域、超限像素平均法更有效。

vue3 reactive定义的变量,快速赋值对象里所有属性,且不丢调响应式

Object.assign(ruleForm, result) let ruleForm = reactive({ applicantName: '', applicantCorporation: '', applicantCorporationNumber: '', applicantCreditCode: '', applicantBusinessLicense: '', cashPoolId: '', totalAmount: 0, loanRateLimit: 80, interestMethod: '', dateTime: [], beginDate: '', endDate: '', interestRate: 0, chargesRate: 0, securityDepositRate: 0, overdueInterestRate: 0, gracePeriod: 0, bankInfoList: [], contractInfoList: [] }) // 详情 const getApplicantDetail = async () => { const params = { id: route.query.id } const { code, message, result } = await applicantDetail(params) if (code !

20个高级Java面试题汇总

1. 什么是可变参数? 可变参数允许调用参数数量不同的方法。请看下面例子中的求和方法。此方法可以调用1个int参数,或2个int参数,或多个int参数。 //int(type) followed ... (three dot's) is syntax of a variable argument. public int sum(int... numbers) { //inside the method a variable argument is similar to an array. //number can be treated as if it is declared as int[] numbers; int sum = 0; for (int number: numbers) { sum += number; } return sum; } public static void main(String[] args) { VariableArgumentExamples example = new VariableArgumentExamples(); //3 Arguments System.

中国地面气候资料日值数据集(V3.0)数据说明以及数据处理

(1)<中国地面气候资料/日值数据集(V3.0)>数据说明 "<中国地面气候资料/日值数据集(V3.0)>“数据是按年-月存放的, 每年每月的所有站点数据每一项气象要素都放在一个文本文件中,全国站点空间位置以及每个文本的详细内容如下: 1. 本站气压:SURF_CLI_CHN_MUL_DAY-PRS-10004-YYYYMM.TXT 2. 气温:SURF_CLI_CHN_MUL_DAY-TEM-12001-YYYYMM.TXT 3. 相对湿度:SURF_CLI_CHN_MUL_DAY-RHU-13003-YYYYMM.TXT 4. 降水:SURF_CLI_CHN_MUL_DAY-PRE-13011-YYYYMM.TXT 5. 蒸发:SURF_CLI_CHN_MUL_DAY-EVP-13240-YYYYMM.TXT 6. 风向风速:SURF_CLI_CHN_MUL_DAY-WIN-11002-YYYYMM.TXT 7. 日照:SURF_CLI_CHN_MUL_DAY-SSD-14032-YYYYMM.TXT 8. 0cm地温:SURF_CLI_CHN_MUL_DAY-GST-12030-0cm-YYYYMM.TXT #特征值说明#: ①台站海拔高度 +100000 当台站海拔高度为估测值时,在估测数据基础上加100000 ②各要素项 32766 数据缺测或无观测任务 ③气压日极值 +20000 气压极值取自定时值,在原值上加20000 ④日最小相对湿度 +300 最小相对湿度取自定时值,在原值上加300 ⑤风速 +1000 当风速超过仪器测量上限时,在上限数据基础上加1000 ⑥风向 1-17 用数字表示风向方位,17表示静风;+100 当表示风向为八风向时,在原值上加100;90X 风向出现X个时,风向数据用个数X表示;95X 风向至少出现X个时,风向数据用个数X表示 ⑦降水量 32700 表示降水"微量”;32XXX XXX为纯雾露霜;31XXX XXX为雨和雪的总量;30XXX XXX为雪量(仅包括雨夹雪,雪暴) ⑧蒸发量 32700 表示蒸发器结冰; +1000 蒸发器中注入的水全部蒸发,在注入的水量数据基础上加1000 ⑨ 0cm地温 +10000 实际温度(零上)超仪器上限刻度,在上限数据基础上加10000; -10000 实际温度(零下)超仪器下限刻度,在下限数据基础上减10000 文本文件的原始数据单位: 气温(0.1°C), 湿度(1%), 风速(0.1m/s), 日照(0.1h), 降水量(0.1mm), 蒸发量(0.1mm), 气压(0.1hPa), 纬度(度分), 经度(度分), 高程(0.1m)

cas单点登录-服务端部署

一.需求描述 公司开发系统越来越多,每个系统都有自己的登录认证流程,给用户很繁琐的体验,统一认证单点登录迫在眉睫 二.流程图 三.本地运行cas服务端 1.拉取cas服务端代码,切换到对应分支(我使用的是6.4.x) https://github.com/apereo/cas-overlay-template 2.由于6.4版本默认jdk11以上,所以在idea进行如下配置 3.在build.gradle文件加入mysql、jdbc依赖配置,并下载依赖 implementation "org.apereo.cas:cas-server-support-json-service-registry:${project.'cas.version'}" implementation "org.apereo.cas:cas-server-support-jdbc:${project.'cas.version'}" implementation "org.apereo.cas:cas-server-support-jdbc-drivers:${project.'cas.version'}" implementation "mysql:mysql-connector-java:5.1.46" 4.yml配置数据源以及http请求支持 cas: authn: jdbc: query[0]: sql: SELECT * FROM user WHERE username = ? url: jdbc:mysql://xxxxxx:3306/cas?useUnicode=true&characterEncoding=utf8&useSSL=false user: root password: xxxx fieldPassword: password driverClass: com.mysql.jdbc.Driver password-encoder: type: DEFAULT encoding-algorithm: MD5 character-encoding: UTF-8 fieldExpired: expired #是否提示改密码的字段 fieldDisabled: disabled #是否禁用用户的字段 accept: enabled: false tgc: secure: false service-registry: core: init-from-json: true json: location: file:/etc/cas/services #cas.authn.accept 指定的就是默认登录用户casuser, 密码Mellon,enabled设为false,则默认用户便不能登录 #如果 cas.

ClickHouse数据查询处理高级技巧

本文介绍ClickHouse查询远程服务、多表联合查询、查询特定分区,以及如何交换表和软删除表。 查询远程ClickHouse服务 如果有多个ClickHouse 集群,可能需要发送远程请求查询。ClickHouse很容易实现,通过内置函数即可实现。 可以在from子句中使用remoteSecure 和 remote ,读取远程服务。语法如下: cluster('cluster_name', db.table[, sharding_key]) cluster('cluster_name', db, table[, sharding_key]) clusterAllReplicas('cluster_name', db.table[, sharding_key]) clusterAllReplicas('cluster_name', db, table[, sharding_key]) 下面举例通过remote函数实现远程查询: SELECT count() FROM remote('127.0.0.1:9000', 'default.t1') AS t WHERE date >= today() Query id: 1868afed-9689-4605-9675-4be4a7725ea4 ┌─count()─┐ │ 14191 │ └─────────┘ 如果以及配置远程服务作为集群,可以发送查询给整个集群,使用clusterAllReplicas函数,举例: parallel_replicas Query id: e53b1851-3246-4d40-8c95-687d3fde5958 Row 1: ────── hostName(): host-db-2 database: database_name table: table_name mutation_id: 0000000004 command: MATERIALIZE TTL create_time: 2022-09-19 16:34:24 block_numbers.partition_id: ['all'] block_numbers.number: [238358] parts_to_do_names: ['all_164055_173528_6_184171'] parts_to_do: 1 is_done: 0 latest_failed_part: latest_fail_time: 1970-01-01 00:00:00 latest_fail_reason: 1 rows in set.

clickhouse插入最佳实践

背景: 对于使用clickhouse来说,和使用mysql等数据库相比,比较需要注意一点是它的数据插入,由于clickhouse后台使用合并各个part分区的方式进行数据合并,所以也就意味着对于ck来说分批插入才是更好的数据插入方式,本文就来对比下客户端分配和使用clickhouse字段的异步插入的优缺点,此处我们只讨论Replicate复制表. 客户端分批 我们可以通过在客户端对数据进行预先分批,比如我们可以在客户端缓存1000条记录,然后再一次性的通过insert values(…)的方式批量插入到ck中 1.这种方式的优点是ck的自动去重功能还在,也就是说如果客户端由于网络原因重试多次插入同一个statement语句的sql,那么ck会自动的去重. 2.关于客户端分批的大小,clickhouse官网建议每批的最小大小至少为1000,比较合理的区间是1w-10w之间,并且每秒插入数不超过1次,也就是每秒最多执行一次insert操作 ck自带的异步插入 使用类似如下的方式开启异步插入以及等待异步插入完成才返回: INSERT INTO YourTable SETTINGS async_insert=1, wait_for_async_insert=1 VALUES (... 首先async_insert=1表示开启异步插入的功能,也即是客户端insert的数据会被ck缓存,直到满足一下条件之一才会写入ck的part分区中, async_insert_max_data_size 缓存大小大于这个配置值async_insert_busy_timeout_ms超时时间到async_insert_max_query_number每个数据块已经达到配置的insert次数 其次,我们看一下wait_for_async_insert参数, a.如果这个参数值=1,那么表示客户端插入数据到ck的缓存后,ck服务器不会马上返回响应,服务器只有再缓存flush数据到part分区后才会返回响应,其实从这里可以看出如果要采用=1的参数值,那么应该是使用多线程插入的方式,因为如果单线程的话,明显每一次其实都是等待async_insert_busy_timeout_ms超时时间到才返回响应. b.如果这个参数=0,那么表示客户端插入数据到ck的缓存后,ck服务器马上返回响应,不会等待缓存的数据flush成part数据,这种方式的弊端是如果服务器在将缓存数据flush成part分区的时候发生错误,那么客户端完全感知不到,此外,如果客户端此时在服务器返回响应后又继续以高频率发送insert数据请求,会导致服务器端的负载压力过高. 其实从这里可知,不论wait_for_async_insert的值是什么,客户端都要控制insert的速度,对于=1来说需要控制多线程insert的速度,对于=0来说,控制单线程insert的速度,不过对于=1来说,Ack确认返回后确切的知道数据已经插入成功了. 最后,异步插入不支持数据去重,也就是当多次重试调用相同的insert语句时,数据会插入多次,而不会进行去重逻辑. 参考文献: https://clickhouse.com/docs/en/optimize/asynchronous-inserts https://clickhouse.com/docs/en/optimize/bulk-inserts

使用PreparedStatement实现批量插入操作,三种方法(批量插入2000条数据为例)

修改、删除本身具有批量操作效果 批量操作主要针对插入操作。 mysql服务器默认关闭批量处理操作的,需要通过一个参数让mysql开启批量处理操作, 将&rewriteBatchedStatementstrue写在配置文件的url后面,配置文件参考: 数据库连接的五种方式_qq_46053741的博客-CSDN博客 代码中jdbcConnection类的构造查看:封装数据库的连接关闭操作_qq_46053741的博客-CSDN博客 使用更新的mysql驱动:mysql-connector-java-5.1.37-bin.jar goods表结构如下: 批量插入操作代码如下: import java.sql.Connection; import java.sql.PreparedStatement; import org.junit.Test; import com3preparedstatement.jdbc.jdbcConnection; public class InsertTest { //方法一: @Test public void testQuer1(){ //1、获取连接 Connection conn=null; PreparedStatement ps=null; try { conn = jdbcConnection.connection(); //2、创建sql语句,实例化PreparedStatement对象 String sql="insert into goods(name)values(?)"; ps = conn.prepareStatement(sql); //3、填充占位符 for(int i=0;i<2000;i++){ ps.setObject(1, "method1_"+i); //4、执行 ps.execute(); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } //5、关闭资源 jdbcConnection.CloseResource(conn, ps); } //方法二:使用addBatch()、executeBatch()、clearBatch()函数,分批次提交sql语句 @Test public void testQuery2(){ //1、获取连接 Connection conn=null; PreparedStatement ps=null; try { conn = jdbcConnection.

实战演练 | MySQL 或其他 DBMS 执行批量插入的三种方式

最近,我写了一个 node.js 脚本,每天要循环访问数百万个文件,并将其内容插入 MySQL 数据库。该脚本不是一次处理一条记录,而是将文件内容存储在内存中,然后每 1000 个文件运行一次 INSERT 语句。为此,我使用了 INSERT 语句的批量插入形式。你可以根据你的特定要求选择使用其他解决方案。在今天的文章中,我们将介绍一些替代方案。 批量插入的 INSERT 语句变体 INSERT 语句支持多种语法变体,其中之一是用于同时插入多行。为此,我们只需要将每个值列表括在括号中并使用逗号将它们分开: INSERT INTO table_name (column_list) VALUES (value_list_1), (value_list_2), ... (value_list_n); 很简单。以下是在 Navicat for MySQL 显示的示例语句: 上面的语句经过格式化以提高可读性,所以在动态生成 SQL 时你不必担心语句的可读性。只要语法在语义上正确,它就可以正常工作。最后,请注意,使用 INSERT 语句一次只可以插入最多 1000 行数据。 LOAD DATA INFILE 对于不希望编写脚本代码的人来说,另一种选择是使用类似于 LOAD DATA INFILE 的命令。这是一个 MySQL 特定的命令,但是大多数其他数据库系统(DBMS)也支持类似的命令。它可以导入各种带分隔符的文件格式,包括逗号(CSV)、制表符(TDV)等。 以下是将“c:\tmp\discounts.csv”文件的数据导入 discounts 表的语句: LOAD DATA INFILE 'c:/tmp/discounts.csv' INTO TABLE discounts FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 ROWS; 在上面的语句中,使用了 IGNORE 1 ROWS 选项来忽略标题。

kotlin定义类,并用where子句指定泛型的约束条件

open class BaseViewHolder { open fun sayHi() { println("BaseViewHolder sayHi!") } } class VH : BaseViewHolder(), Expandable { override fun sayHi() { super.sayHi() println("VH sayHi!") } override fun doExpand() { println("doExpand") } override fun doCustom() { println("doCustom") } } interface Expandable { fun doExpand() fun doCustom() } class KeepOneHolder<VH>(val myVH: VH) where VH : BaseViewHolder, VH : Expandable { fun bind(viewHolder: VH) { viewHolder.sayHi() } } fun main() { println("

oracle中使用sql语句新增表字段(多字段)

oracle中使用sql语句新增表字段(多字段) 1、多字段新增语句 ALTER TABLE T_XXXXXX_XXXX ADD ( TEST1 VARCHAR2(255), TEST2 VARCHAR2(255), TEST3 VARCHAR2(2000), TEST4 VARCHAR2(2000) ); COMMENT ON COLUMN T_XXXXXX_XXXX .TEST1 IS '如果控制点是扣罚类型的时候:扣罚金额'; COMMENT ON COLUMN T_XXXXXX_XXXX .TEST2 IS '控制点源端数据编号'; COMMENT ON COLUMN T_XXXXXX_XXXX .TEST3 IS '协办人'; COMMENT ON COLUMN T_XXXXXX_XXXX .TEST4 IS '协办部门'; 2、单字段新增 ALTER TABLE T_XXXXXX_XXXX ADD (TEST1 VARCHAR2(255)); COMMENT ON COLUMN T_XXXXXX_XXXX .TEST1 IS '如果控制点是扣罚类型的时候:扣罚金额';

解锁Android开发利器:MVVM架构

前言: 作为Android开发者,我们都希望能够开发出高效、可维护和可测试的应用。而MVVM(Model-View-ViewModel)架构正是我们的得力助手。在本文中,我将详细介绍MVVM架构的原理和流程,并通过一个示例演示如何使用MVVM进行应用开发。让我们一起解锁Android开发的利器! 一、MVVM架构的原理 MVVM架构的核心思想是将应用程序划分为三个主要部分:Model、View和ViewModel。这三者之间的交互和数据流如下图所示: Model(模型):Model层负责管理应用程序的数据和业务逻辑。它可以是数据库、网络请求、文件系统等数据源,并提供数据的获取、更新和处理方法。View(视图):View层负责展示用户界面,并处理用户的交互操作。它可以是Activity、Fragment或自定义的View组件。ViewModel(视图模型):ViewModel层作为连接View和Model的桥梁。它负责从Model中获取数据,并将数据转换为View可以使用的形式。ViewModel还处理用户交互事件,并将这些事件转化为更新Model或View的操作。 MVVM架构的关键概念是数据绑定(Data Binding)。通过数据绑定,ViewModel可以将数据直接绑定到View上,使得View能够自动更新,并且保持与ViewModel的同步。 二、MVVM架构流程 让我们通过一个示例来演示MVVM架构的工作流程。我们将开发一个天气应用,展示实时天气信息,并提供城市切换功能。 定义Model: data class Weather(val city: String, val temperature: String) class WeatherRepository { fun getWeather(city: String): Weather { // 从网络或其他数据源获取天气数据 return Weather(city, "25°C") } } 定义View: class WeatherActivity : AppCompatActivity() { private lateinit var viewModel: WeatherViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_weather) viewModel = ViewModelProvider(this).get(WeatherViewModel::class.java) viewModel.weather.observe(this, { weather -> updateUI(weather) }) val cityButton = findViewById<Button>(R.id.cityButton) cityButton.setOnClickListener { // 用户点击城市按钮时触发切换城市的操作 val selectedCity = // 通过弹出对话框或其他方式获取用户选择的城市 viewModel.

在Git上删除本地分支名称/远程分支名称

文章目录 前言一、Git删除本地分支名称1.确保你不在要删除的分支上。可以使用git checkout命令切换到其他分支。例如,切换到 master 分支:2.使用git branch -d命令删除本地分支。如果分支已经被合并,可以使用 -d选项。 二、Git删除远程分支名称1.使用git push命令,例如:删除名为origin的远程仓库上的test_2.0分支2.使用git fetch -p命令更新本地的远程跟踪分支列表,确保已删除的远程分支不再显示 前言 Git删除本地分支名称、远程分支名称 一、Git删除本地分支名称 打开idea软件的Terminal控制台 1.确保你不在要删除的分支上。可以使用git checkout命令切换到其他分支。例如,切换到 master 分支: 2.使用git branch -d命令删除本地分支。如果分支已经被合并,可以使用 -d选项。 二、Git删除远程分支名称 1.使用git push命令,例如:删除名为origin的远程仓库上的test_2.0分支 origin是远程仓库的别名,test_2.0是要删除的远程分支名称 2.使用git fetch -p命令更新本地的远程跟踪分支列表,确保已删除的远程分支不再显示

在Git上更改本地分支名称/远程分支名称

文章目录 前言一、Git更改本地分支名称1.使用“ git branch ”命令检查当前在哪个分支上。2.使用“ git branch -m "test_2.0" ”命令将当前所在的分支更改为"test_2.0" 二、Git更改远程分支名称1.使用“ git push origin :"test_1.0" "test_2.0" ”命令将"test_1.0"分支从远程删除,且在远程上创建"test_2.0"分支2.最后使用“ git push origin -u test_2.0 ” 命令,将test_2.0分支进行push操作 前言 Git更改本地分支名称、远程分支名称 一、Git更改本地分支名称 打开idea软件的Terminal控制台 1.使用“ git branch ”命令检查当前在哪个分支上。 在这种情况下,我们已经在"test_1.0"分支上,假设需要修改此分支的名称,不需要切换到任何其它分支 2.使用“ git branch -m “test_2.0” ”命令将当前所在的分支更改为"test_2.0" 到此,成功更改了Git上的本地分支名称! “本地”分支名称已更改为“ test_2.0”,但这并不意味着你的分支名称已在远程上更新! 二、Git更改远程分支名称 接着上面的流程继续操作,需要将重新更改后的分支“ test_2.0”推送到远程仓库。 1.使用“ git push origin :“test_1.0” “test_2.0” ”命令将"test_1.0"分支从远程删除,且在远程上创建"test_2.0"分支 这句命令,实际进行了两个操作: a:将"test_1.0"分支从远程删除 b:在远程上创建"test_2.0"分支 2.最后使用“ git push origin -u test_2.0 ” 命令,将test_2.0分支进行push操作 到此,成功更改了Git上的远程分支名称!

前端面试:Vue中给对象添加新属性页面不刷新原因及解决方案

vue2是用过Object.defineProperty实现数据响应式 const obj = {} Object.defineProperty(obj, 'foo', { get() { console.log(`get foo:${val}`); return val }, set(newVal) { if (newVal !== val) { console.log(`set foo:${newVal}`); val = newVal } } }) } 当我们访问foo属性或者设置foo值的时候都能够触发setter与getter obj.foo obj.foo = 'new' 但是我们为obj添加新属性的时候,却无法触发事件属性的拦截 obj.bar = '新属性' 原因是一开始obj的foo属性被设成了响应式数据,而bar是后面新增的属性,并没有通过Object.defineProperty设置成响应式数据 解决方案 Vue.set() Vue.set( target, propertyName/index, value ) Object.assign() 直接使用Object.assign()添加到对象的新属性不会触发更新。应创建一个新的对象,合并原对象和混入对象的属性 this.someObject = Object.assign({},this.someObject,{newProperty1:1,newProperty2:2 ...}) $forceUpdate 如果你发现你自己需要在 Vue 中做一次强制更新,99.9% 的情况,是你在某个地方做错了事 $forceUpdate迫使 Vue 实例重新渲染(不推荐使用)

顺序查找(C语言)

一.不带哨兵: 1. 线性表结构体 typedef struct List { int *data;//元素数组 int length;//可以写入多少元素 int num;//目前元素个数 }List; 2.初始化列表 List *initList(int length) {/*参数length:有多少元素空位*/ List *list = (List*)malloc(sizeof(List));//为列表申请空间 list->length = length;//元素长度写入 list->data = (int*)malloc(sizeof(int)*length);//为列表内的数组申请空间 list->num = 0;//初始元素个数为0 return list; //返回列表 } 3.向列表内写入元素 void listAdd(List *list,int data) {/*参数一:列表指针 参数二:要写入的元素*/ list->data[list->num] = data;//将元素按序写入 list->num += 1;//列表内元素数量加一 } 4.查找元素 int search(List *list,int key) {/*参数一:列表指针 参数二:要查找的元素*/ int i; for(i=0;i<list->num;i++)//循环遍历列表中的每一个元素直到与要查找的元素值相等 { if(list->data[i]==key) return key; } return -1;//没查到返回-1 } 5.遍历输出列表内的元素 void printList(List *list) {/*参数list:列表指针*/ int i; for(i=0;i<list->num;i++)//循环遍历,按序输出列表内data的值 printf("

JVM总结

JVM体系 JVM的位置 JVM是运行在操作系统之上的,它与硬件没有直接的交互 整体结构 程序计数器 是一个非常小的内存空间,几乎可以忽略不记。 每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码,通过改变计数器的值来读取指令。 在多线程的情况下,程序计数器⽤于记录当前线程执⾏的位置,从⽽当线程被切换回来的时候能够知道该线程上次运⾏到哪⼉了。 CPU中的程序计数器和JVM中的PC CPU中的程序计数器(PC) CPU中的PC是一个大小为一个字的存储设备(寄存器),在任何时候,PC中存储的都是内存地址(是不是有点像指针?),而CPU就根据PC中的内存地址,到相应的内存取出指令然后执行并且更新PC的值。在计算机通电后这个过程会一直不断的反复进行。计算机的核心也在于此。 ​ JAVA运行时数据区域程序计数器 在CPU中PC是一个物理设备,而java中PC则是一个一块比较小的内存空间,它是当前线程字节码执行的行号指示器。在java的概念模型中,字节码解释器就是通过改变这个计数器中的值来选取下一条执行的字节码指令的,它的程序控制流的指示器,分支,线程恢复等功能都依赖于这个计数器。 ​ 我们知道多线程的实现是多个线程轮流占用CPU而实现的,而在线程切换的时候就需要保存当前线程的执行状态,这样在这个线程重新占用CPU的时候才能恢复到之前的状态,而在JVM状态的保存是依赖于PC实现的,所以PC是线程所私有的内存区域,这个区域也是java运行时数据区域唯一不会发生OOM的区域 ​ 栈 栈内存,主管程序的运行,生命周期和线程同步; 线程结束,栈内存也就释放,对于栈来说,不存在垃圾回收问题。一旦线程结束,栈就over Java 虚拟机栈是由⼀个个栈帧组成,⽽每个栈帧中都拥有:局部变量表、操作数栈、动态链接、⽅法出⼝信息。 栈里有:8大基本类型+对象引用+实例的方法 程序正在执行的方法,一定在栈的顶部 会产生两类异常: StackOverflowError:当线程请求的栈深度超过了虚拟机允许的最大深度时抛出。 OutOfMemoryError:如果 JVM 栈容量可以动态扩展,虚拟机栈占用内存超出抛出。 本地方法栈 为虚拟机使⽤到的 Native ⽅法服务 Native 凡是带了native关键字的,说明Java的作用范围达不到了,会去调用底层C语言的库! 会进入本地方法栈 调用本地方法接口 JNI JNI作用:拓展Java的使用,融合不同的编程语言为Java所用!(Java诞生时,C、C++横行,必须要有能调用他们的程序) 它在内存区域中专门开辟了一块标记区域:本地方法栈,登记native方法 最终执行的时候,通过JNI加载本地方法库中的方法。 Java程序驱动打印机,管理系统时会用到(现在),在企业级应用中较为少见! 堆和栈 有什么区别? (1)申请方式 stack:由系统自动分配,声明在函数中一个局部变量int b; 系统自动在栈中为b开辟空间 heap:需要程序员自己申请,并指明大小。手动new Object() (2)申请后系统的响应 stack:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出 heap:操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 (3)申请大小的限制 stack:栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M,如果申请的空间超过栈的剩余空间时,将提示 overflow。因此,能从栈获得的空间较小。 heap:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。 (4)申请效率的比较 stack:由系统自动分配,速度较快。但程序员是无法控制的。 heap:由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。 (5)heap和stack中的存储内容 stack:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,注意静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。 heap:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。 方法区 方法区是被所有线程共享,静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中。 static、final、Class模板,常量池 永久代 永久代是 HotSpot 的概念,⽅法区是 Java 虚拟机规范中的定义,是⼀种规范,⽽永久代是⼀种实现