内存缓存Caffeine基本使用姿势介绍

SpringBoot集成系列--Caffeine 本文介绍了如何使用Spring缓存、Caffeine缓存以及Caffeine与Redis结合实现二级缓存。首先,介绍了Spring缓存的基本概念和实现方式,然后详细阐述了如何使用Caffeine缓存库进行缓存操作,包括定义配置类和使用方法。最后,介绍了如何通过Caffeine与Redis结合实现二级缓存,并抽取公共代码以方便重用。 阅读详情

Caffeine作为当下本地缓存的王者被大量的应用再实际的项目中,可以有效的提高服务吞吐率、qps,降低rt

本文将简单介绍下Caffeine的使用姿势

项目配置

1. 依赖

首先搭建一个标准的SpringBoot项目工程,相关版本以及依赖如下

本项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发

<dependencies><dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId></dependency></dependencies>

使用实例

引入上面的jar包之后,就可以进入caffeine的使用环节了;我们主要依照官方wiki来进行演练

Home zh CN · ben-manes/caffeine Wiki

caffeine提供了四种缓存策略,主要是基于手动添加/自动添加,同步/异步来进行区分

其基本使用姿势于Guava差不多

1. 手动加载

private LoadingCache<String, Integer> autoCache;
private AtomicInteger idGen;
publicCacheService() {
      // 手动缓存加载方式
      idGen = newAtomicInteger(100);
      uidCache = Caffeine.newBuilder()
              // 设置写入后五分钟失效
              .expireAfterWrite(5, TimeUnit.MINUTES)
              // 设置最多的缓存数量
              .maximumSize(100)
              .build();
}
1.1 三种失效策略

注意参数设置,我们先看一下失效策略,共有下面几种

权重:

  • maximumSize: 基于容量策略,当缓存内元素个数超过时,通过基于就近度和频率的算法来驱逐掉不会再被使用到的元素

  • maximumWeight: 基于权重的容量策略,主要应用于缓存中的元素存在不同的权重场景

时间:

  • expireAfterAccess: 基于访问时间

  • expireAfterWrite: 基于写入时间

  • expireAfter: 可以根据读更新写入来调整有效期

引用:

  • weakKeys: 保存的key为弱引用

  • weakValues: 保存的value会使用弱引用

  • softValues: 保存的value使用软引用

弱引用:这允许在GC的过程中,当没有被任何强引用指向的时候去将缓存元素回收

软引用:在GC过程中被软引用的对象将会被通过LRU算法回收

1.2 缓存增删查姿势

接下来我们看一下手动方式的使用

publicvoidgetUid(String session) {
    // 重新再取一次,这次应该就不是重新初始化了Integeruid= uidCache.getIfPresent(session);
    System.out.println("查看缓存! 当没有的时候返回的是 uid: " + uid);

    // 第二个参数表示当不存在时,初始化一个,并写入缓存中
    uid = uidCache.get(session, (key) -> 10);
    System.out.println("初始化一个之后,返回的是: " + uid);

    // 移除缓存
    uidCache.invalidate(session);

    // 手动添加一个缓存
    uidCache.put(session + "_2", 11);

    // 查看所有的额缓存Mapmap= uidCache.asMap();
    System.out.println("total: " + map);

    // 干掉所有的缓存
    uidCache.invalidateAll();
}

查询缓存&添加缓存

  • getIfPresent(key): 不存在时,返回null

  • get(key, (key) -> {value初始化策略}): 不存在时,会根据第二个lambda表达式来写入数据,这个就表示的是手动加载缓存

  • asMap: 获取缓存所有数据

添加缓存

  • put(key, val): 主动添加缓存

清空缓存

  • invalidate: 主动移除缓存

  • invalidateAll: 失效所有缓存

执行完毕之后,输出日志:

查看缓存! 当没有的时候返回的是 uid: null
初始化一个之后,返回的是: 10
total: {02228476-bcd9-412d-b437-bf0092c4a5f6_2=11}

2. 自动加载

在创建的时候,就指定缓存未命中时的加载规则

// 在创建时,自动指定加载规则private LoadingCache<String, Integer> autoCache;
private AtomicInteger idGen;

publicCacheService() {
    // 手动缓存加载方式
    idGen = newAtomicInteger(100);
    autoCache = Caffeine.newBuilder()
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .maximumSize(100)
            .build(newCacheLoader<String, Integer>() {
                @Overridepublic@Nullable Integer load(@NonNull String key)throws Exception {
                    return idGen.getAndAdd(1);
                }
            });
}

它的配置,与前面介绍的一致;主要的区别点在于build时,确定缓存值的获取方式

2.1 缓存使用姿势
publicvoidautoGetUid(String session) {
    Integeruid= autoCache.getIfPresent(session);
    System.out.println("自动加载,没有时返回: " + uid);

    uid = autoCache.get(session);
    System.out.println("自动加载,没有时自动加载一个: " + uid);

    // 批量查询
    List<String> keys = Arrays.asList(session, session + "_1");
    Map<String, Integer> map = autoCache.getAll(keys);
    System.out.println("批量获取,一个存在一个不存在时:" + map);

    // 手动加一个
    autoCache.put(session + "_2", 11);
    Maptotal= autoCache.asMap();
    System.out.println("total: " + total);
}

与前面的区别在于获取缓存值的方式

  • get(key): 不用传第二个参数,直接传key获取对应的缓存值,如果没有自动加载数据

  • getAll(keys): 可以批量获取数据,若某个key不再缓存中,会自动加载;在里面的则直接使用缓存的

实际输出结果如下

自动加载,没有时返回: null
自动加载,没有时自动加载一个: 100
批量获取,一个存在一个不存在时:{02228476-bcd9-412d-b437-bf0092c4a5f6=100, 02228476-bcd9-412d-b437-bf0092c4a5f6_1=101}
total: {02228476-bcd9-412d-b437-bf0092c4a5f6_2=11, 02228476-bcd9-412d-b437-bf0092c4a5f6_1=101, 02228476-bcd9-412d-b437-bf0092c4a5f6=100}
复制代码

3.异步手动加载

异步,主要是值在获取换粗内容时,采用的异步策略;使用与前面没有什么太大差别

// 手动异步加载缓存private AsyncCache<String, Integer> asyncUidCache;

publicCacheService() {
    asyncUidCache = Caffeine.newBuilder()
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .maximumSize(100)
            .buildAsync();
}
复制代码
3.1 缓存使用姿势
publicvoidasyncGetUid(String session)throws ExecutionException, InterruptedException {
    // 重新再取一次,这次应该就不是重新初始化了
    CompletableFuture<Integer> uid = asyncUidCache.getIfPresent(session);
    System.out.println("查看缓存! 当没有的时候返回的是 uid: " + (uid == null ? "null" : uid.get()));

    // 第二个参数表示当不存在时,初始化一个,并写入缓存中
    uid = asyncUidCache.get(session, (key) -> 10);
    System.out.println("初始化一个之后,返回的是: " + uid.get());

    // 手动塞入一个缓存
    asyncUidCache.put(session + "_2", CompletableFuture.supplyAsync(() -> 12));

    // 移除缓存
    asyncUidCache.synchronous().invalidate(session);
    // 查看所有的额缓存
    System.out.println("print total cache:");
    for (Map.Entry<String, CompletableFuture<Integer>> sub : asyncUidCache.asMap().entrySet()) {
        System.out.println(sub.getKey() + "==>" + sub.getValue().get());
    }
    System.out.println("total over");
}
复制代码
  • getIfPresent: 存在时返回CompletableFuture,不存在时返回null,因此注意npe的问题

  • get(key, Function<>): 第二个参数表示加载数据的逻辑

  • put(key, CompletableFuture<>): 手动加入缓存,注意这里也不是直接加一个具体的value到缓存

  • synchronous().invalidate() : 同步清除缓存

  • getAll: 一次获取多个缓存,同样的是在缓存的取缓存,不在的根据第二个传参进行加载

与前面相比,使用姿势差不多,唯一注意的是,获取的并不是直接的结果,而是CompletableFuture,上面执行之后的输出如下:

查看缓存! 当没有的时候返回的是 uid: null
初始化一个之后,返回的是: 10
print total cache:
5dd53310-aec7-42a5-957e-f7492719c29d_2==>12
total over
复制代码

4. 异步自动加载

在定义缓存时,就指定了缓存不存在的加载逻辑;与第二个相比区别在于这里是异步加载数据到缓存中

private AtomicInteger idGen;
// 自动异步加载缓存private AsyncLoadingCache<String, Integer> asyncAutoCache;
publicCacheService() {
  idGen = newAtomicInteger(100);
  asyncAutoCache = Caffeine.newBuilder()
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .maximumSize(100)
            .buildAsync(newCacheLoader<String, Integer>() {
                @Overridepublic@Nullable Integer load(@NonNull String key)throws Exception {
                    return idGen.getAndAdd(1);
                }
            });
}
复制代码
4.1 缓存使用姿势
publicvoidasyncAutoGetUid(String session) {
    try {
        CompletableFuture<Integer> uid = asyncAutoCache.getIfPresent(session);
        System.out.println("自动加载,没有时返回: " + (uid == null ? "null" : uid.get()));

        uid = asyncAutoCache.get(session);
        System.out.println("自动加载,没有时自动加载一个: " + uid.get());

        // 批量查询
        List<String> keys = Arrays.asList(session, session + "_1");
        CompletableFuture<Map<String, Integer>> map = asyncAutoCache.getAll(keys);
        System.out.println("批量获取,一个存在一个不存在时:" + map.get());
        
        // 手动加一个
        asyncAutoCache.put(session + "_2", CompletableFuture.supplyAsync(() -> 11));
        
        // 查看所有的额缓存
        System.out.println("print total cache:");
        for (Map.Entry<String, CompletableFuture<Integer>> sub : asyncAutoCache.asMap().entrySet()) {
            System.out.println(sub.getKey() + "==>" + sub.getValue().get());
        }
        System.out.println("total over");

        // 清空所有缓存
        asyncAutoCache.synchronous().invalidateAll();
      } catch (Exception e) {
        e.printStackTrace();
    }
}
复制代码

输出:

自动加载,没有时返回: null
自动加载,没有时自动加载一个: 102
批量获取,一个存在一个不存在时:{5dd53310-aec7-42a5-957e-f7492719c29d=102, 5dd53310-aec7-42a5-957e-f7492719c29d_1=103}
print total cache:
5dd53310-aec7-42a5-957e-f7492719c29d_2==>11
5dd53310-aec7-42a5-957e-f7492719c29d_1==>103
5dd53310-aec7-42a5-957e-f7492719c29d==>102
total over

作者:一灰灰

链接:https://juejin.cn/post/7213733567606898745

来源:稀土掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

高性能缓存Caffeine的基本使用方式 Caffeine是基于JDK1.8版本的高性能本地缓存库,它是Guava的增强版,与ConcurrentLinkedHashMap相似,支持并发,并且可以在O(1)的时间复杂度内查找、写入元素。 阅读详情

相关推荐

SpringBoot 集成 Caffeine 实现本地缓存

Caffeine 官网Caffeine 是基于Java 1.8 的高性能本地缓存库,同样是 Google 开发的,由 Guava 改进而来,底层设计思路、功能和使用方式与 Guava 非常类似,但是各方面的性能都要远远超过前者,可以看做是 Guava cache 的升级版。而且在 Spring5 开始的默认缓存实现就将 Caffeine 代替原来的 Google Guava,官方说明指出,其缓存命中率已经接近最优值@Data@Service@Autowired// 模拟数据库数据。

sco5282的博客 7764

Caffeine本地缓存Demo

Gauva和Caffeine的区别 - Gauva基于LRU算法,Caffeine基于W-TinyLFU算法(结合了LRU和LFU的特点) - Caffeine支持异步加载 - Caffeine性能和开销都优于Guava - Caffeine的命中率更高

李杰的博客 982

android+音乐节拍检测,科学网—音乐节拍跟踪或音乐节拍检测软件,LilyBeats alpha - 石自强的博文...

我们设计并实现了一种用于实时跟踪音乐节拍的软件。该软件可以用于智能灯方案提供商,为其提供灯光随音乐变化的核心解决方案。首先给出软件的下载链接:或者音乐节拍是乐曲中表示固定单位时值和强弱规律的组织形式,亦称拍子。它有两个特性:周期性和连续性。节拍周期性表现为节拍结构,是乐曲中周期性出现的节奏序列。节拍连续性表现为音乐的平均速度,其单位为bpm(beat per minute每分钟音乐节拍的个数)。音...

weixin_39571403的博客 1401

java缓存框架Caffeine详解

本地缓存是指将数据存储在设备的硬盘、内存或闪存中,通过空间换时间的策略来减少从远程服务器获取数据的时间和网络开销。Caffeine是一个基于Java8开发的提供了近乎最佳命中率的高性能缓存库。可以说是目前最优秀的本地缓存,性能对比可以参考链接:。实际上,Caffeine和ConcurrentMap很像——支持并发,并且支持O(1)时间复杂度的数据存取。ConcurrentMap将存储所有存入的数据,直到你将其手动移除;Caffeine将通过给定的配置,自动移除“不常用”的数据,以保持内存的合理占用。

asxyxxx的博客 1万+

从业10年的AI应用架构师:企业AI平台运营的4个核心经验,值得收藏

去年年底,我在某大型制造企业做AI咨询时,遇到了这样的场景:这不是个例。我接触过的10家企业中,有7家的AI平台都陷入了“技术自嗨”的陷阱——技术团队追求模型精度、功能完备性,却忽略了业务到底需要什么。如果你也遇到过类似问题:比如“模型准确率很高,但业务不买账”“AI项目上线后没人用”“投入产出比极低”,那么这篇文章的4个经验,可能会帮你避开这些坑。很多企业认为,AI平台就是“买一套工具+部署几个模型”。但实际上,企业AI平台的本质是“技术与业务的连接生态”:根据Gartner的报告,80%的企业AI项目失

AI 原生应用开发的博客 709

基于SiamFC的红外目标跟踪

初始化阶段:在第一帧中,通过用户标注获得目标框,从目标区域中提取一个固定大小的模板(称为 exemplar)。搜索阶段:在后续帧中,在以当前目标中心为中心的较大区域(称为 instance)内进行搜索,将模板与搜索区域通过卷积操作得到响应图。位置更新:在响应图中找到峰值位置,并转换到原图坐标,用以更新目标中心。部分实现中还会考虑多尺度搜索和目标尺寸更新。import cv2# 定义 SiamFC 简易网络(示例)# 特征提取部分,注意红外图像通常为单通道,使用较少的卷积核数提高实时性。

weixin_43745234的博客 774

caffeine缓存的使用

caffeine

qq_44871116的博客 3958

Hadoop在大数据领域的电商数据分析案例

随着电商行业的迅猛发展,每天都会产生海量的数据,如用户浏览记录、购买行为、商品信息等。如何从这些数据中提取有价值的信息,为电商企业的决策提供支持,成为了电商企业面临的重要问题。Hadoop作为一个开源的分布式计算平台,具有高可靠性、高扩展性和高效性等特点,能够很好地处理大数据。本文的目的就是通过实际案例,详细介绍Hadoop在电商数据分析中的应用,范围涵盖了从数据采集、存储到分析的整个流程。本文首先介绍背景信息,让读者了解Hadoop在电商数据分析中的重要性和应用场景。

Java大师兄的博客 1178

本地缓存Caffeine

Caffine 是一款高性能的近似LFU(最近最少频率使用)准入策略的本地缓存组件,Caffeine的底层数据存储采用ConcurrentHashMap,使用 Window TinyLfu 回收策略,提供了一个近乎最佳的命中率。适用范围:变更频率低、实时性要求低的数据之前的 removalListener 是异步监听,此处的 writer 方法可以设置同步监听器,同步监听器一个实现了接口 CacheWriter 的实例化对象,我们需要自定义接口的实现类。

m0_57801749的博客 1万+

SAP CPI 之使用国密算法实现SM系列算法加解密

System.out.println("SM2-私钥:" + privateKey);System.out.println("SM2-公钥:" + publicKey);System.out.println("SM2-密文:" + encrypts);System.out.println("SM2-明文:" + data2);新建groovy,通过import之后,调用jar包中的方法。在CP中导入jar包,选择Upload,选中导入的jar包。的密钥和向量值,不安全!的公钥和私钥,不安全!

u012830826的博客 1335

在 ABAP CDS View 里把 Conversion Functions 用到极致:FLTP 精度、单位换算、货币换算与小数位处理的工程化实践

数据格式转换在真实项目中比业务逻辑更容易引发问题。ABAP CDS中的转换函数(如FLTP_TO_DEC、UNIT_CONVERSION、CURRENCY_CONVERSION和DECIMAL_SHIFT)可帮助在数据库层统一数据口径,避免应用层重复处理。这些函数支持数值精度治理、单位换算、货币转换和小数位调整,能显著提升报表性能并减少逻辑分散。通过将格式转换前移到数据模型层,可以确保服务层和UI层的数据一致性,同时降低接口传输错误风险。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 2351

Caffeine 缓存库的常用功能使用介绍

Caffeine作为新一代高性能Java缓存库,在并发场景下展现出卓越表现。它通过创新的W-TinyLFU淘汰算法实现高达99%的命中率,并采用无锁设计使吞吐量较传统方案提升5-10倍。该库提供灵活的缓存管理能力:支持基于时间(写入/访问过期)、数量或权重的淘汰策略;允许为单个Key设置专属过期时间;独创的异步刷新机制能在不阻塞请求的情况下更新数据。开发者可通过简洁的链式API配置内存控制、加载逻辑和事件监听,轻松构建高并发低延迟的智能缓存系统。其与Guava Cache兼容的接口设计,更使迁移

在技术的广袤天地里,本博客如精准罗盘。剖析前沿科技,深掘代码奥秘,以精炼笔触,带您穿越复杂技术迷宫,速达知识彼岸。 2453

TC3XX - MCAL知识点(三十五):Tasking编译器下,Tc3xx的MCAL代码如何使用多核,多核中断、多核任务、多核外设之间的MCAL配置与代码例程

文章用于记录在裸机情况下使用多核Tc387芯片,如何高效运行以及正确的代码、工程属性、链接文件配置等问题,相互结合、相互影响的地方,以示后续使用记录。

梦想技术家 962

Java-缓存-内存缓存Caffeine基本使用

Caffeine作为当下本地缓存的王者被大量的应用再实际的项目中,可以有效的提高服务吞吐率、qps,降低rt本文将简单介绍下Caffeine的使用姿势。

Java程序员的知识博客 1799

windows下宝塔安装教程

windows下宝塔安装教程 目录宝塔是什么?宝塔安装流程1.下载2.配置3.登录4.安装的环境套件小结 宝塔是什么? 宝塔是管理服务器的免费的软件,宝塔上面可以进行对你的服务器批量进行管理,对于运维人员来说还是必不可少的一个软件,值得使用。 宝塔安装流程 1.下载 链接: https://download.bt.cn/win/panel/BtSoft.zip 下载后安装买最好自定义目录,方便找 2.配置 下载完成后会出现这个页面,对着页面进行配置 点击默认密码复制进来,这里注意一点就是一定要查看

你在看屏幕的同时,屏幕也在注视着你 4423

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Java内存缓存神器:Caffeine(咖啡因)

Caffeine是一个基于Java8开发的提供了近乎最佳命中率的高性能的缓存库。缓存和ConcurrentMap有点相似,但还是有所区别。最根本的区别是ConcurrentMap将会持有所有加入到缓存当中的元素,直到它们被从缓存当中手动移除。但是,Caffeine的缓存Cache 通常会被配置成自动驱逐缓存中元素,以限制其内存占用。在某些场景下,LoadingCache和AsyncLoadingCache 因为其自动加载缓存的能力将会变得非常实用。

秃了也弱了 1万+

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

本地缓存Caffeine使用教程

Caffeine是一个基于Java1. 8的高性能本地缓存库。它提供了灵活的缓存配置选项,如缓存大小限制、过期策略、驱逐策略等。Caffeine是由guava演变而来的,性能和内存占用都优于guava,它的底层数据存储采用ConcurrentHashMap,从功能上可以将Caffeine视为带有淘汰策略的ConcurrentHashMap。Caffeine的Github网址。

tttalk的博客 5237

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

Caffeine缓存 最快缓存 内存缓存

一、序言 Caffeine是一个进程内部缓存框架。 对比Guava Cache Caffeine是在Guava Cache的基础上做一层封装,性能有明显提高,二者同属于内存级本地缓存。使用Caffeine后无需使用Guava Cache,从并发的角度来讲,Caffeine明显优于Guava,原因是使用了Java 8最新的StampedLock锁技术。 二、缓存简介 (一)缓存对比 从横向对常用的缓存进行对比,有助于加深对缓存的理解,有助于提高技术选型的合理性。下面对比三种常用缓存:Redis、EhCach.

Java知识图谱的博客 4348

Java最强本地缓存——Caffeine

Caffeine是基于Java 1.8的高性能本地缓存库,由 Guava 演变而来,它的性能比Guava也更好,官方声称在基准测试中,Caffeine 的缓存命中率已经接近于最优值,且Caffeine 的内存占用情况也优于 Guava实际上,Caffeine 这种本地缓存和 ConcurrentHashMap 很像——都支持并发,都支持 O(1) 时间复杂度的存取。ConcurrentHashMap 会存储所有存入的数据,且数据移除需要进行显式的操作。

Yonagi的博客 9770

【学习笔记】本地缓存Caffeine介绍及使用

在java开发过程中,缓存是我们经常会谈到的一个话题,而在java领域有个缓存框架号称本地缓存性能之王, Caffeine。

泽济天下的专栏 4181

Caffeine缓存指南

Caffeine是一个由Ben Manes开发的高性能缓存库,灵感来源于Guava Cache,并在其基础上进行了大量的性能优化。Caffeine提供了一种近乎最优的缓存解决方案,具有低延迟、高吞吐量、灵活配置等特点。支持多种缓存策略,如LRU、LFU等提供细粒度的过期控制和自动刷新机制内置统计监控功能,便于性能调优易于集成,提供简单直观的API缓存是一种常见的性能优化技术,通过在内存中存储经常访问的数据,减少对数据库或其他外部数据源的访问,从而提高系统的响应速度。缓存策略。

漫天飞雪 3378

Caffeine史上最快的内存缓存

咖啡因(Caffeine)在技术领域通常不是指咖啡中的刺激性成分,而是指一个高性能的Java缓存库。

奇遇少年 1415

本地缓存之王-Caffeine

引言 随着业务体量的增长,使用的缓存方案一般会经过:1)无缓存直接查DB;2)数据同步+Redis;3)多级缓存 三个阶段。 第1阶段直接查DB只能用于小流量场景,随着QPS升高,需要引入缓存来减轻DB压力。 第2阶段一般会通过消息队列将数据同步到Redis,并在数据发生变更时同步更新Redis,业务查询时直接查Redis,如果Redis无数据再去查DB。该方案的缺点是如果Redis发生故障,缓存雪崩会直接将流量打到DB,可能会进一步将DB打挂,导致业务事故。 第3阶段将Redis缓存作为二级缓存,在其上再

诗酒年华 2万+
上一篇: 阿里最新题库!阿里发布Java社招岗(正式版)面试题
下一篇: 优秀! 腾讯技术官推出Java零基础入门宝典,助你快速掌握编程技能
Java技术那些事儿
博客等级 码龄5年 2306粉丝 2584原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值