Google Guava 缓存工具使用详解

文章目录

  • 缓存工具
    • Cache接口
    • LoadingCache接口
    • CacheBuilder类
    • CacheLoader类
    • CacheStats类
    • RemovalListener类


缓存工具

Guava提供了Cache接口和相关的类来支持缓存功能,它提供了高性能、线程安全的内存缓存,可以用于优化应用程序的性能。

特点:

  • 自动回收过期数据
  • 支持缓存项的定时回收
  • 支持缓存项的最大数量限制
  • 支持缓存项的大小限制
  • 支持缓存项的权重限制,简化开发者实现基于容量的限制
  • 提供了统计信息

相关接口类:

接口/类描述
Cache<K, V>接口表示一种能够存储键值对的缓存结构
LoadingCache<K, V>是 Cache 接口的子接口,用于在缓存中自动加载缓存项
CacheLoader<K, V>在使用 LoadingCache 时提供加载缓存项的逻辑
CacheBuilder用于创建 CacheLoadingCache 实例的构建器类
CacheStats用于表示缓存的统计信息,如命中次数、命中率、加载次数、存储次数等
RemovalListener<K, V>用于监听缓存条目被移除的事件,并在条目被移除时执行相应的操作

使用示例:

    public static void main(String[] args) throws Exception {// 创建Cache实例LoadingCache<String, String> cache = CacheBuilder.newBuilder().initialCapacity(2)                                         // 设置初始容量.concurrencyLevel(4)                                        // 设置并发级别.maximumSize(5)                                             // 设置最大容量
//                .maximumWeight(1000)                                        // 设置最大权重
//                .weigher((Weigher<String, String>) (k, v) -> v.length())    // 设置权重计算器.expireAfterWrite(Duration.ofSeconds(3))                    // 写入后3秒过期.expireAfterAccess(Duration.ofSeconds(20))                  // 访问后20秒过期.refreshAfterWrite(Duration.ofSeconds(10))                  // 写入后自动刷新,3秒刷新一次.recordStats()                                              // 开启统计信息记录.removalListener(notification -> {                          // 设置移除监听// 缓存Key被移除时触发String cause = "";if (RemovalCause.EXPLICIT.equals(notification.getCause())) {cause = "被显式移除";} else if (RemovalCause.REPLACED.equals(notification.getCause())) {cause = "被替换";} else if (RemovalCause.EXPIRED.equals(notification.getCause())) {cause = "被过期移除";} else if (RemovalCause.SIZE.equals(notification.getCause())) {cause = "被缓存条数超上限移除";} else if (RemovalCause.COLLECTED.equals(notification.getCause())) {cause = "被垃圾回收移除";}System.out.println(DateUtil.formatDateTime(new Date()) + " Key: " + notification.getKey() + " 移除了, 移除原因: " + cause);}).build(new CacheLoader<String, String>() {                  // 设置缓存重新加载逻辑@Overridepublic String load(String key) {// 重新加载指定Key的值String newValue = "value" + (int)Math.random()*100;System.out.println(DateUtil.formatDateTime(new Date()) + " Key: " + key + " 重新加载,新value:" + newValue);return newValue;}});// 将数据放入缓存cache.put("key0", "value0");cache.invalidate("key0");cache.put("key1", "value1");cache.put("key1", "value11");cache.put("key2", "value22");cache.put("key3", "value3");cache.put("key4", "value4");cache.put("key5", "value5");cache.put("key6", "value6");cache.put("key7", "value7");cache.put("key8", "value8");while (true) {// 获取数据System.out.println(DateUtil.formatDateTime(new Date()) + " get key1 value: " + cache.get("key1"));// 统计信息System.out.println(DateUtil.formatDateTime(new Date()) + " get stats: " + cache.stats());Thread.sleep(1000);}}

打印日志:

2023-11-24 15:48:17 Key: key0 移除了, 移除原因: 被显式移除
2023-11-24 15:48:17 Key: key1 移除了, 移除原因: 被替换
2023-11-24 15:48:17 Key: key1 移除了, 移除原因: 被缓存条数超上限移除
2023-11-24 15:48:17 Key: key2 移除了, 移除原因: 被缓存条数超上限移除
2023-11-24 15:48:17 Key: key3 移除了, 移除原因: 被缓存条数超上限移除
2023-11-24 15:48:17 Key: key1 重新加载,新value:value0
2023-11-24 15:48:17 Key: key4 移除了, 移除原因: 被缓存条数超上限移除
2023-11-24 15:48:17 get key1 value: value0
2023-11-24 15:48:17 get stats: CacheStats{hitCount=0, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=3083100, evictionCount=4}
2023-11-24 15:48:18 get key1 value: value0
2023-11-24 15:48:18 get stats: CacheStats{hitCount=1, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=3083100, evictionCount=4}
2023-11-24 15:48:19 get key1 value: value0
2023-11-24 15:48:19 get stats: CacheStats{hitCount=2, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=3083100, evictionCount=4}
2023-11-24 15:48:20 Key: key5 移除了, 移除原因: 被过期移除
2023-11-24 15:48:20 Key: key6 移除了, 移除原因: 被过期移除
2023-11-24 15:48:20 Key: key7 移除了, 移除原因: 被过期移除
2023-11-24 15:48:20 Key: key8 移除了, 移除原因: 被过期移除
2023-11-24 15:48:20 Key: key1 移除了, 移除原因: 被过期移除
2023-11-24 15:48:20 Key: key1 重新加载,新value:value0
2023-11-24 15:48:20 get key1 value: value0
2023-11-24 15:48:20 get stats: CacheStats{hitCount=2, missCount=2, loadSuccessCount=2, loadExceptionCount=0, totalLoadTime=3154100, evictionCount=9}
2023-11-24 15:48:21 get key1 value: value0
2023-11-24 15:48:21 get stats: CacheStats{hitCount=3, missCount=2, loadSuccessCount=2, loadExceptionCount=0, totalLoadTime=3154100, evictionCount=9}
2023-11-24 15:48:22 get key1 value: value0
2023-11-24 15:48:22 get stats: CacheStats{hitCount=4, missCount=2, loadSuccessCount=2, loadExceptionCount=0, totalLoadTime=3154100, evictionCount=9}
2023-11-24 15:48:23 Key: key1 移除了, 移除原因: 被过期移除
2023-11-24 15:48:23 Key: key1 重新加载,新value:value0
2023-11-24 15:48:23 get key1 value: value0
2023-11-24 15:48:23 get stats: CacheStats{hitCount=4, missCount=3, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=3208400, evictionCount=10}
2023-11-24 15:48:24 get key1 value: value0
2023-11-24 15:48:24 get stats: CacheStats{hitCount=5, missCount=3, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=3208400, evictionCount=10}
2023-11-24 15:48:25 get key1 value: value0
2023-11-24 15:48:25 get stats: CacheStats{hitCount=6, missCount=3, loadSuccessCount=3, loadExceptionCount=0, totalLoadTime=3208400, evictionCount=10}
......

Cache接口

Cache接口是Guava缓存的核心接口,定义了缓存的基本操作。

它是使用 CacheBuilder 创建的。它提供了基本的缓存操作,如 put、get、delete等方法。同时,Cache 还提供了诸如统计信息、缓存项的值获取方式、缓存项的失效、缓存项的回收等方法,可以满足大多数应用的需求。

主要方法:

方法描述
V get(K key, Callable<? extends V> valueLoader)根据键获取对应的缓存值,如果缓存中不存在该键,会使用 valueLoader 加载并存储该值。
V getIfPresent(K key)根据键获取对应的缓存值,如果不存在则返回 null。
Map<K, V> getAllPresent(Iterable<?> keys)获取多个键对应的缓存值的映射,如果缓存中不存在某个键,则该键不会出现在返回的映射中。
void put(K key, V value)将键值对放入缓存中。如果键已经存在,则会替换对应的值。
void putAll(Map<? extends K, ? extends V> map)将多个键值对添加到缓存中。
void invalidate(Object key)根据键从缓存中移除条目。
void invalidateAll(Iterable<?> keys)根据键集合移除多个条目。
void invalidateAll()移除缓存中的所有条目。
long size()返回缓存中的条目数。
CacheStats stats()返回缓存的统计信息。
ConcurrentMap<K, V> asMap()返回缓存的并发映射视图。
void cleanUp()执行缓存的清理操作。

LoadingCache接口

LoadingCache 继承自 Cache 接口,它是一个带有自动加载功能的缓存接口。

在使用 LoadingCache 时,如果缓存中不存在所需的键值对,则会自动调用CacheLoader的加载方法进行加载,并将加载的结果存入缓存中。

主要方法:

方法说明
get(K key)根据指定的键检索值,如果键不存在,将调用 CacheLoader 进行加载并返回对应的值
getAll(Iterable<? extends K> keys)根据给定的键集合批量检索值,并返回一个 Map 对象,对于已缓存的键将直接返回对应的值,对于未缓存的键将通过 CacheLoader 进行加载。
getUnchecked(K key)获取指定键对应的值,如果缓存中不存在该键,则返回 null,不会触发CacheLoader 加载。
refresh(K key)刷新指定键对应的值,即使用 CacheLoader 重新加载该键对应的值,并更新缓存。

CacheBuilder类

CacheBuilder类是用于创建Guava缓存的构建器。可以使用该类的newBuilder()方法创建一个构建器实例,并通过一系列方法设置缓存的属性,例如最大容量、过期时间等。最后可以通过build()方法构建一个Cache实例。

主要方法:

方法说明
newBuilder()创建一个新的 CacheBuilder 实例
from(CacheBuilderSpec spec)根据给定的规范字符串创建一个 CacheBuilder 实例
from(String spec)根据给定的规范字符串创建一个 CacheBuilder 实例
initialCapacity(int initialCapacity)设置缓存的初始容量
concurrencyLevel(int concurrencyLevel)设置并发级别,用于估计同时写入的线程数
maximumSize(long maximumSize)设置缓存的最大容量
maximumWeight(long maximumWeight)设置缓存的最大权重
weigher(Weigher<? super K1, ? super V1> weigher)设置缓存的权重计算器
weakKeys()使用弱引用存储缓存键(例如,键的引用没有被其他对象引用时,可以被垃圾回收)
weakValues()使用弱引用存储缓存值(例如,值的引用没有被其他对象引用时,可以被垃圾回收)
softValues()使用软引用存储缓存值(例如,当内存不足时,可以被垃圾回收)
expireAfterWrite(java.time.Duration duration)设置写入后过期时间
expireAfterWrite(long duration, TimeUnit unit)设置写入后过期时间
expireAfterAccess(java.time.Duration duration)设置访问后过期时间
expireAfterAccess(long duration, TimeUnit unit)设置访问后过期时间
refreshAfterWrite(java.time.Duration duration)设置写入后自动刷新时间
refreshAfterWrite(long duration, TimeUnit unit)设置写入后自动刷新时间
ticker(Ticker ticker)设置用于衡量缓存时间的时钟源
removalListener(RemovalListener<? super K1, ? super V1> listener)设置缓存条目移除监听器
recordStats()开启缓存统计信息记录
build(CacheLoader<? super K1, V1> loader)使用指定的 CacheLoader 构建缓存
build()构建缓存,如果没有指定 CacheLoader,则需要使用 get 方法手动加载缓存项

部分方法详解:

  • initialCapacity:设置缓存的初始容量

    这个方法将通过一个整数值设置缓存的初始大小。它是一个可选的方法,如果没有指定,缓存将采用默认的初始容量。

  • concurrencyLevel:设置并发级别

    用于估计同时写入的线程数。这个方法将通过一个整数值设置并发级别,用于内部数据结构的调整,以提高并发写入的性能。它是一个可选的方法,缺省值为 4。

  • maximumSize:设置缓存的最大容量

    这个方法将通过一个 long 类型的值设置缓存的最大容量。当缓存的条目数达到这个容量时,会触发缓存清除策略来移除一些条目以腾出空间。它是一个可选的方法,如果没有指定最大容量,缓存将不会有大小限制。

  • maximumWeight:设置缓存的最大权重

    这个方法将通过一个 long 类型的值设置缓存的最大权重。权重可以根据缓存条目的大小计算,通常用于缓存对象大小不同的场景。当缓存的总权重达到这个值时,会触发缓存清除策略来移除一些条目以腾出空间。它是一个可选的方法,如果没有指定最大权重,缓存将不会有权重限制。

  • weigher:设置缓存的权重计算器

    这个方法将通过一个实现了 Weigher 接口的对象设置缓存的权重计算器。通过权重计算器,可以根据缓存条目的键和值来计算它们的权重,以便在达到最大权重时触发缓存清除策略。它是一个可选的方法,如果没有设置权重计算器,缓存将不会有权重限制。

  • expireAfterWrite:设置写入后过期时间

    这个方法通过一个 java.time.Duration 对象设置缓存条目的写入后过期时间。过期时间从最后一次写入条目开始计算。一旦超过指定的时间,条目将被认为是过期的并被清除。这是一个可选的方法,如果没有指定过期时间,条目将不会主动过期。

  • expireAfterAccess:设置访问后过期时间

    这个方法通过一个 java.time.Duration 对象设置缓存条目的访问后过期时间。过期时间从最后一次访问条目开始计算。一旦超过指定的时间,条目将被认为是过期的并被清除。这是一个可选的方法,如果没有指定过期时间,条目将不会主动过期。

  • refreshAfterWrite:设置写入后自动刷新时间

    这个方法通过一个 java.time.Duration 对象设置缓存条目的自动刷新时间。自动刷新时间从最后一次写入条目开始计算。一旦超过指定的时间,当条目被访问时,缓存将自动刷新该条目,即会调用 CacheLoader 的 load 方法重新加载该条目。这是一个可选的方法,如果没有设置自动刷新时间,条目将不会自动刷新。

  • recordStats():开启缓存统计信息记录

    这个方法用于开启缓存的统计信息记录功能。一旦开启,可以通过 Cache.stats() 方法获取缓存的统计信息,如命中率、加载次数、平均加载时间等。这是一个可选的方法,如果不开启统计信息记录,将无法获取缓存的统计信息。

注意事项:

  • maximumSize与maximumWeight不能同时设置

  • 设置maximumWeight时必须设置weigher

  • 当缓存失效后,refreshAfterWrite设置的写入后自动刷新时间不会再有用

    注意:expireAfterWrite、expireAfterAccess、refreshAfterWrite三个值的使用

  • 开启recordStats后,才进行统计

CacheLoader类

CacheLoader 可以被视为一种从存储系统(如磁盘、数据库或远程节点)中加载数据的方法。

CacheLoader 通常搭配refreshAfterWrite使用,在写入指定的时间周期后会调用CacheLoader 的load方法来获取并刷新为新值。

load 方法在以下情况下会被触发调用:

  • 当设置了refreshAfterWrite(写入后自动刷新时间),达到自动刷新时间时,会调用 load 方法来重新加载该键的值。

  • 调用 Cache.get(key) 方法获取缓存中指定键的值时,如果该键的值不存在,则会调用 load 方法来加载该键的值。

  • 调用 Cache.get(key, callable) 方法获取缓存中指定键的值时,如果该键的值存在,则直接返回;如果该键的值不存在,则会调用 callable 参数指定的回调函数来计算并加载该键的值。

  • 调用 Cache.getUnchecked(key) 方法获取缓存中指定键的值时,无论该键的值存在与否,都会调用 load 方法来加载该键的值。

需要注意的是,当调用 load 方法加载缓存值时,可能会发生 IO 操作或其他耗时操作,因此建议在加载操作中使用异步方式来避免阻塞主线程。另外,加载操作的实现要考虑缓存的一致性和并发性,避免多个线程同时加载同一个键的值。

CacheStats类

CacheStats 对象提供了诸如缓存命中率、加载缓存项数、缓存项回收数等统计信息的访问。

它可以通过 Cache.stats() 方法来获取,从而方便开发者监控缓存状态。

主要属性:

属性描述
hitCount缓存命中次数。表示从缓存中成功获取数据的次数
missCount缓存未命中次数。表示从缓存中未能获取到数据的次数
loadSuccessCount加载数据成功次数。表示通过 CacheLoader 成功加载数据的次数
loadExceptionCount加载数据异常次数。表示通过 CacheLoader 加载数据时发生异常的次数
totalLoadTime加载数据总耗时。表示通过 CacheLoader 加载数据的总时间
evictionCount缓存项被移除的次数,只记录因空超过设置的最大容量而进行缓存项移除的次数

RemovalListener类

RemovalListener 用于在缓存中某个值被移除时执行相应的回调操作。

可以使用 CacheBuilder.removalListener() 方法为缓存设置 RemovalListener。

RemovalListener 的使用:

  1. 创建一个实现 RemovalListener 接口的类,实现 onRemoval 方法。这个方法会在缓存项被移除时被调用,接受两个参数: key 和 value。key 是被移除的缓存项的键,value 是被移除的缓存项的值。你可以根据需要在 onRemoval 方法中实现自定义的逻辑。

  2. 使用 CacheBuilder 的 removalListener 方法,将创建的 RemovalListener 对象传递给它。

  3. 缓存项被移除时,onRemoval 方法会自动被调用,方法会传入一个RemovalNotification 类型的参数,里面包含相应的 key 和 value等信息。你可以在这个方法中执行自定义的业务逻辑,例如日志记录、资源清理等操作。

    RemovalNotification:

    RemovalNotification 是 Guava 中用于表示缓存项被移除的通知的类。当在 Guava Cache 中注册了 RemovalListener 后,RemovalNotification 对象会在缓存项被移除时传递给 RemovalListener 的 onRemoval 方法。

    RemovalNotification 包含了有关被移除缓存项的一些重要信息,例如键、值以及移除原因。下面是 RemovalNotification 类中常用的属性和方法:

    • getKey():获取被移除的缓存项的键。

    • getValue():获取被移除的缓存项的值。

    • getCause():获取移除原因,它是一个枚举类型RemovalCause,表示缓存项被移除的原因。

      RemovalCause的可选值:

      • EXPLICIT:条目被显式删除,例如通过调用 Cache.invalidate(key) 方法。
      • REPLACED:条目被替换,例如通过调用 Cache.put(key, value) 方法重复放入相同的键。
      • EXPIRED:缓存条目由于达到了指定的过期时间而被移除。
      • SIZE:缓存条目由于超过了指定的大小限制而被移除。
      • COLLECTED:缓存条目被垃圾回收移除。这是在启用了缓存值的弱引用或软引用时发生的。

    使用 RemovalNotification 可以让你在缓存项被移除时获取相关信息,并根据移除原因采取适当的处理措施。例如,你可以根据移除原因记录日志、执行清理操作、发送通知等。这样能够增强缓存的功能和可观察性。

注意事项:

  • RemovalListener 的 onRemoval 方法会在移除操作发生时同步调用,因此请确保不要在此方法中做耗时的操作,以免阻塞缓存的性能。
  • 如果在缓存移除过程中抛出任何异常,它将被捕获并记录,不会影响缓存的正常运行。
  • 需要注意的是,RemovalListener 只会在通过 Cache 的操作(如 invalidate、invalidateAll、put 进行替换)触发移除时被调用,并不会在缓存项因为过期失效而自动移除时被调用

使用 RemovalListener 可以方便地在缓存项被移除时执行一些自定义的操作,例如清理相关资源、更新其他缓存或发送通知等。根据实际需要,合理利用 RemovalListener 可以增强缓存的功能和灵活性。

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

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

相关文章

Flutter 控件查阅清单

为了方便记录和使用Flutter中的各种控件&#xff0c;特写此博客以记之&#xff0c;好记性不如烂笔头嘛&#xff1a;&#xff09; 通过控件的首字母进行查找&#xff0c;本文会持续更新 控件目录 AAppBar BCContainerColumn &#xff08;列&#xff09; DDivider (分割线) EElev…

oj赛氪练习题,

区间内的真素数 import java.util.ArrayList; import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);int M scanner.nextInt();int N scanner.nextInt();scanner.close();ArrayList<Integer>…

Google Guava 散列工具使用详解

文章目录 散列哈希函数哈希码布隆过滤器 散列 Guava 提供了一组散列&#xff08;哈希&#xff09;相关的工具类和方法&#xff0c;包括哈希函数接口、哈希算法实现、哈希码&#xff08;HashCode&#xff09;类、布隆过滤器&#xff08;BloomFilter&#xff09;等等。 Guava 提…

【大连民族大学C语言CG题库练习题】——组合1

【问题描述】 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 【输入形式】 【输出形式】 【样例输入】 4 2 【样例输出】 [2,4] [3,4] [2,3] [1,2] [1,3] [1,4] 【样例说明】 【评分标准】 代码思路&a…

吴恩达《机器学习》11-1-11-2:首先要做什么、误差分析

一、首先要做什么 选择特征向量的关键决策 以垃圾邮件分类器算法为例&#xff0c;首先需要决定如何选择和表达特征向量 &#x1d465;。视频提到的一个示例是构建一个由 100 个最常出现在垃圾邮件中的词构成的列表&#xff0c;根据这些词是否在邮件中出现来创建特征向量&…

Java CompletableFuture使用示例

在我之前的文章IO密集型服务提升性能的三种方法中提到过&#xff0c;提升IO密集型应用性能有个方式就是异步编程&#xff0c;实现异步时一定会用到Future&#xff0c;使用多线程Future我们可以让多个任务同时去执行&#xff0c;最后统一去获取执行结果&#xff0c;这样整体执行…

Docker下安装可视化工具Portainer

目录 Portainer简介 Portainer安装 Portainer简介 Portainer是一款开源的容器管理平台&#xff0c;支持多种容器技术&#xff0c;如Docker、Kubernetes和Swarm等。它提供了一个易于使用的Web UI界面&#xff0c;可用于管理和监控容器和集群。Portainer旨在使容器管理更加简单…

如何在Ubuntu上安装pip3

一、检查系统是否安装pip3 首先&#xff0c;我们需要检查系统中是否已经安装了pip3。为此&#xff0c;我们可以运行以下命令&#xff1a; pip3 --version如果系统已经安装了pip3&#xff0c;您将会看到输出结果类似于&#xff1a; pip 19.0.3 from /usr/local/lib/python3.6…

内部类Lambda

静态内部类 /*** 静态成员是在类加载成字节码时就已经存在的,静态只能访问静态*/ public class Demo {public static void main(String[] args) {Outer.Inner.show();} }class Outer {int num1 10;static int num2 20;static class Inner {static void show() {Outer outer …

Kubernetes实战(六)-多系统架构容器镜像构建实战

1 背景 最近在一个国产化项目中遇到了这样一个场景&#xff0c;在同一个 Kubernetes 集群中的节点是混合架构的&#xff0c;即其中某些节点的 CPU 架构是 x86 的&#xff0c;而另一些节点是 ARM 的。为了让镜像在这样的环境下运行&#xff0c;一种最简单的做法是根据节点类型为…

6-15 复制字符串

#include<stdio.h> #include<string.h> int main(){int i;char s1[80],s2[80];printf("输入的s2是&#xff1a;");scanf("%s",s2);for(i0;i<strlen(s2);i)s1[i]s2[i];printf("复制后的s1是&#xff1a;%s\n",s1); return 0;}

ES如何提高召回率之【词干提取】

想要提高召回率就需要尽可能匹配相关的文档&#xff0c;其中一个办法就是在索引阶段对词语分析&#xff08;分词器&#xff09;的时候提取词干&#xff0c;搜索的时候也取词干。 不取词干 es默认使用的是标准的分词器&#xff0c;是不会取词干的。 但是标准分词器是包含小写转…

HttpRunner自动化工具之实现参数化传递

参数化实现及重复执行 参数化测试&#xff1a;在接口测试中&#xff0c;为了实现不同组数据对同一个功能模块进行测试&#xff0c;需要准备多组测试数据对模块进行测试的过程。 在httprunner中可以通过如下方式实现参数化&#xff1a; 1、在YAML/JSON 中直接指定参数列表 2、…

【STM32】STM32学习笔记-STM32简介(02)

00. 目录 文章目录 00. 目录01. STM32简介1.1 STM32是什么1.2 STM32应用领域1.3 STM32命名规则1.4 STM32选型 02. ARM简介2.1 ARM是什么2.2 ARM系列 03. STM32开发板3.1 MCU简介3.2 STM32开发板3.3 STM32硬件资源 04. STM32系统架构05. STM32引脚定义06. STM32启动配置07. STM3…

深入探讨Java设计模式:构建灵活而可维护的代码

深入探讨Java设计模式&#xff1a;构建灵活而可维护的代码 设计模式是在软件设计中&#xff0c;经过反复验证&#xff0c;以解决特定问题的最佳实践的经验总结。在Java中&#xff0c;设计模式是一种强大的工具&#xff0c;用于构建可扩展、灵活且易于维护的代码。本文将深入讨…

构建第一个ArkTS应用(纯HarmonyOS应用)

1. 安装开发工具 在华为开发者官方上下载HarmonyOS应用专用的开发工具&#xff0c;链接地址&#xff1a;HUAWEI DevEco Studio和SDK下载和升级 | HarmonyOS开发者 要想使用开发工具让项目跑起来&#xff0c;需要10G的磁盘空间。开发工具需要的磁盘空间为2.36G&#xff1b;SDK需…

WT2003H语音芯片系列:通过bin文件实现板载语音更新,支持宽范围音频码率

随着科技的飞速发展&#xff0c;语音芯片已经成为了许多电子产品不可或缺的一部分。在这个领域中&#xff0c;WT2003H语音芯片系列以其卓越的性能和灵活的功能而备受瞩目。这一系列芯片具备一种独特的功能&#xff0c;即可以通过bin文件在板更新语音&#xff0c;同时音频码率支…

深度学习记录--logistic回归函数的计算图

计算图用于logistic回归函数 先回顾一下单一样本的logistic回归损失函数的公式&#xff0c;公式如下&#xff1a; 将logistic函数用计算图表示出来(以两个基础量为例)&#xff0c;计算图如下&#xff1a; 前向传播已经完成&#xff0c;接下来完成后向传播 运用链式法则依次求…

The Big IAM Challenge 云安全 CTF 挑战赛

The Big IAM Challenge 云安全 CTF 挑战赛 今天&#xff0c;我们来做一下有关于云安全 的CTF 挑战赛 The Big IAM Challenge,旨在让白帽子识别和利用 IAM错误配置&#xff0c;并从现实场景中学习&#xff0c;从而更好的认识和了解IAM相关的风险。比赛包括6个场景&#xff0c;每…

LeetCode 232.用栈实现队列

题目 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾 int pop() 从队列的开头移除并返回元素 int peek() 返回…