Java 项目实战: 交友项目 - 通用缓存SpringCache:问题引入

Anaconda创建python环境常见命令 Anaconda功能庞大,其可以理解为一个工具,也是一个可执行命令。Anaconda可以创建多个python环境,且python环境间相互独立。 阅读详情

概述

小视频列表是全站刷新最频繁的接口,每次请求都要跨 Redis、Mongo、MySQL 三个存储跑一遍。这里用这条真实链路算一笔账,推导出「必须加缓存」的结论,再拆解手写缓存代码的四个毛病,它们正是 SpringCache 要解决的问题。

纲要

  • 问题场景:视频列表接口的三段跨存储开销
  • Redis 读推荐 vid 列表
  • Mongo 分页查视频
  • Dubbo 调用 → MySQL 批查用户资料
  • 数据特征分析:为什么视频列表「适合」缓存(读多写少 + 容忍短暂旧数据)
  • 缓存决策三问:缓存什么、缓存多久、失效怎么办
  • 手写 Redis 缓存的样板代码及其四个问题
  • 解决方向预告:AOP + 注解 = 声明式缓存(SpringCache)
  • 无缓存 vs 有缓存的请求路径对比
  • 常见误解澄清

从视频列表的请求开销说起

在已完成的小视频功能里,SmallVideosService#queryVideoList 是客户端首页的核心接口。用户每滑一下、每次下拉刷新,都会触发一次这个方法。先看它一次请求到底做了什么(此处只看链路,列表查询的完整实现见查询视频列表系列文章):

// 查询视频列表(加缓存前的现状:每次请求都完整走一遍三段链路)
public PageResult queryVideoList(Integer page, Integer pagesize) {

    // 第一段:Redis —— 读该用户的推荐 vid 列表
    String redisKey = Constants.VIDEOS_RECOMMEND + UserHolder.getUserId();
    String redisValue = redisTemplate.opsForValue().get(redisKey);

    List<Video> list = new ArrayList<>();
    int redisPages = 0;
    if (!StringUtils.isEmpty(redisValue)) {
        // Redis 中有推荐列表:按页码截取 vid,再查详情
        String[] values = redisValue.split(",");
        if ((page - 1) * pagesize < values.length) {
            List<Long> vids = Arrays.stream(values).skip((page - 1) * pagesize).limit(pagesize)
                    .map(Long::valueOf)
                    .collect(Collectors.toList());
            list = videoApi.findMovementsByVids(vids);   // 第二段:Mongo 按 vid 批量查
        }
        redisPages = PageUtil.totalPage(values.length, pagesize);
    }
    // Redis 没有推荐数据:直接 Mongo 分页
    if (list.isEmpty()) {
        list = videoApi.queryVideoList(page - redisPages, pagesize);   // 第二段:Mongo 分页
    }

    // 第三段:提取 userId,跨服务批查用户资料
    List<Long> userIds = CollUtil.getFieldValues(list, "userId", Long.class);
    Map<Long, UserInfo> map = userInfoApi.findByIds(userIds, null);    // Dubbo → MySQL

    // 组装 VideoVo ...
    return new PageResult(page, pagesize, 0L, vos);
}

把链路画出来就是这样:

客户端请求视频列表

Redis
读推荐 vid 列表

MongoDB
分页查视频 / 按 vid 批查

Dubbo 远程调用
user-service 批查用户资料

MySQL
user_info 表查询

组装 VideoVo 返回

单次请求看着不重,但这类「刷feed」接口的访问模式是高频、重复、内容短期不变。算一笔账:

假设数值
日活用户10 万
人均日均刷新次数30 次
日请求量300 万
峰值 QPS(按流量集中在 4 小时估算)数百次/秒

也就是说,每秒几百次请求,每一次都完整执行「Redis + Mongo + Dubbo远程调用 + MySQL」三段链路,而其中绝大多数请求拿到的结果和上一次一模一样。Mongo 的分页查询(skip/limit 页码越大越慢)、MySQL 的批量用户查询、Dubbo 的网络往返,全部在重复劳动。数据库的容量是按「存取数据」设计的,不是按「扛流量」设计的,压力最终都会砸在 Mongo 和 MySQL 上。

这就是缓存要解决的问题:把昂贵的查询结果存起来,让重复请求不再穿透到数据库。

这份数据适合缓存吗

不是所有数据都值得缓存。判断标准看两条:读写比例,以及业务对旧数据的容忍度。

读写比例:视频的发布频率是「一个用户几天发一条」,浏览频率是「一个用户一天刷几十次」。读写比悬殊,典型的读多写少。

旧数据容忍度:列表里晚几秒出现一条新视频,用户完全无感知;某条视频的作者改了昵称,列表里旧昵称多显示几分钟也无伤大雅。它不是订单金额、账户余额那种强一致数据。

两条都满足,视频列表就是教科书级的可缓存场景。反例也有:库存数量(写多、强一致)、风控状态(必须实时)就不适合。

给哪里加缓存:三个决策问题

确定要缓存之后,剩下的是三个设计决策。

一、缓存什么? 按「读频率 × 计算成本」给候选数据排序:

候选数据读频率计算成本结论
视频分页列表结果极高(每次刷新)高(三段链路)优先缓存,收益最大
用户资料高(每个列表都要拼)中(跨服务 + MySQL)可缓存,但注意修改场景的失效
互动状态(点赞/喜欢)高低(单次 Redis 读)本身已是缓存层,不必再套一层

二、缓存多久? TTL 是「命中率」和「数据新鲜度」的权衡。TTL 太短(比如几秒),缓存刚写进去过期了,命中率趋近于零,等于白做;TTL 太长(比如一天),用户要等一天才能看到新视频。视频列表这种场景,分钟级 TTL(5~10 分钟)是合理的量级,足够扛住峰值流量,又保证「晚几分钟看到新内容」在业务可接受范围内。用户资料这类更稳定的数据可以给到 30 分钟以上。

三、失效怎么办? 光靠 TTL 过期是被动的:用户发完新视频,自己刷新列表却看不到,要等 TTL 到期,体验不可接受。所以原则是**「写时失效」**:发布视频的写操作执行后,主动删掉列表缓存,下次读请求自然回源重建。TTL 只作兜底,不作唯一手段。

这三个问题的答案,最终会落到两个注解上(缓存、失效各一个)。但在引入框架之前,先看看自己动手写是什么样子。

手写缓存:三段样板代码

不用任何缓存框架,靠 SpringDataRedis 手写,每个需要缓存的方法都要套这个模板。以「查询所有用户」为例:

package com.mf.server.service;

import com.mf.model.domain.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class UserInfoService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private UserInfoApi userInfoApi;

    // 手写缓存查询所有用户:读缓存 → miss 查库 → 写回
    public List<UserInfo> queryAll() {
        // 1、先查缓存
        String key = "USER_INFO_ALL";
        String cacheJson = redisTemplate.opsForValue().get(key);
        if (cacheJson != null && !cacheJson.isEmpty()) {
            // 2、缓存命中:反序列化直接返回
            return JSON.parseArray(cacheJson, UserInfo.class);
        }
        // 3、缓存未命中:查数据库
        List<UserInfo> list = userInfoApi.findAll();
        // 4、写回缓存,并设置过期时间
        redisTemplate.opsForValue().set(key, JSON.toJSONString(list), 10, TimeUnit.MINUTES);
        // 5、返回
        return list;
    }
}

流程本身没问题,问题在于这个模板会以什么方式蔓延。画出来更直观:

命中

未命中

请求进入

查询缓存

反序列化返回

查询数据库

写入缓存 + 设 TTL

返回

结束

绿色那条支路是缓存的全部价值,红色两条支路是每个方法都要重复书写的成本。逐条数一下这种写法的毛病:

样板重复。「查缓存 → 判空 → 查库 → 写回」这五步在每个需要缓存的方法里原样出现一遍。十个接口要缓存,就抄十遍,改 TTL 或序列化方式时十处一起改。

缓存逻辑侵入业务。方法真正的业务可能只有一句 userInfoApi.findAll(),剩下全是缓存操作代码。业务方法和缓存策略被焊死在同一个方法体里,读业务逻辑时必须跳过缓存代码,改缓存策略时必须打开业务类。

key 管理散乱。key 以字符串字面量的形式散落在各个 Service 里,"USER_INFO_ALL"、"VIDEOS_" + userId + "_" + page……没有统一的命名规范,拼错一个下划线就是一次缓存永不命中,而且很难排查。

容易漏失效。写操作和缓存失效是两处独立代码,改用户资料的 update 方法里必须记得删 USER_INFO_ALL 这个 key。新人加一个修改入口忘了删,缓存里就是一份永不过期(直到 TTL 到期)的旧数据,这类 bug 不报错,只有用户投诉才能发现。

还有一个隐性问题:缓存实现和 Redis 绑死。哪天要换成 Memcached 或进程内缓存,每个方法体都要动刀。

解决方向:把缓存从方法体里抽出来

回到本质看上面那段手写代码:方法内真正干活的只有一句查库调用,其余的缓存操作,本质上都是围绕这句调用做的「增强」,查之前先拦一下,命中就短路返回;没命中就放行执行,拿到结果再补写缓存。

「不修改源代码的前提下对方法增强」,这个描述指向的就是动态代理。在 Spring 里,就是 AOP:

缓存命中

缓存未命中

Client

动态代理对象
缓存增强在这一层

直接返回缓存数据
被代理对象根本不执行

调用被代理对象

业务方法:只关注业务逻辑
一句查库调用

代理拿到结果
写入缓存后返回

思路顺理成章,但自己实现还有两个坎:

  • key 怎么统一生成?A 方法用 A 做 key,B 方法用 B,代理层怎么知道每个方法该用什么 key?,在方法上标注解,把 key 的规则写在注解里,让代理解析。
  • 怎么适配多种缓存?Redis、Memcached 接口各异。,面向接口编程,代理层只依赖缓存抽象,换实现只改一处。

把这两件事做规范,就得到了「注解 + AOP + 缓存抽象」的完整方案。这套方案不需要自己从零写,Spring 官方已经做好了,就是 Spring Cache:一个基于 AOP 的声明式缓存框架,方法上加一个注解即具备缓存能力,且不关心底层用的是 Redis 还是别的缓存实现。它和声明式事务 @Transactional 是同一个思想:把横切逻辑交给框架,业务代码只留业务。

具体怎么用 @Cacheable、@CacheEvict 落到视频列表上,将在后续文章《通用缓存SpringCache:介绍以及入门案例》中展开。

常见误解澄清

误解实情
加了缓存一定更快缓存 miss 时反而更慢:多了一次缓存查询 + 序列化开销,然后照样查库。缓存只有在命中率足够高时才是正收益
缓存设好就不用管失效不做失效设计,用户发布视频后自己刷新看不到新内容。「写时失效 + TTL 兜底」是标准组合,缺一不可
所有数据都可以缓存写多读少的数据(库存计数)命中率低白占内存;强一致数据(账户余额)缓存后可能造成资金类事故。缓存前先看读写比和一致性要求
缓存和数据库会自动保持一致不会。缓存是独立存储,任何一次写库都不会自动通知缓存。一致性完全依赖你设计的失效策略
缓存 = RedisRedis 只是常见实现之一。Memcached 同属分布式缓存,Guava/Caffeine 是进程内缓存。Spring Cache 的价值之一就是把这些实现藏在抽象后面

官方文档

总结

  • 视频列表一次请求要串 Redis、Mongo、MySQL 三段开销,刷 feed 场景下高频重复访问,把这个成本放大成数据库的实际压力。
  • 读多写少、容忍短暂旧数据,两个条件同时满足才值得缓存;视频列表恰好都满足,TTL 取分钟级量级,失效靠「写时主动删除 + TTL 兜底」。
  • 手写缓存的五步模板本身没错,错在它必须随接口数量复制:样板重复、侵入业务、key 散乱、易漏失效,且与 Redis 实现绑死。
  • 解法是熟悉的思路,用动态代理把缓存增强从业务方法中抽走,key 和缓存操作写成注解交给代理解析。这就是 Spring Cache 的全部设计动机,后续文章将从入门案例开始动手。
手把手教你用C#和LibUsbDotNet实现USB设备通信(含完整代码示例) 本文详细介绍了如何使用C#和LibUsbDotNet库实现USB设备通信,包括设备识别、连接建立、同步/异步数据读写等核心操作。通过完整的代码示例,开发者可以快速掌握USB通信的关键技术,适用于工业自动化、医疗设备等多种场景。重点讲解了数据读写优化和错误处理技巧,帮助提升通信效率和可靠性。 阅读详情

相关推荐

vue高德地图POI关键字搜索定位

最近在项目中遇到了关于地图的问题,首先为了满足需求从而使用的是JSAPI为2.0版本的。需求是根据关键字搜索定位到该位置。值得注意的是在使用 AMap.Event.addListener时 Event一定要大写的E,不然页面会报错。在地图上定义一个输入框,根据输入的位置进行提示,然后点击后跳转到对应的位置并打点标记。JSAPI主要使用这两个。

YL 2136

SpringBoot交友APP项目实战(详细介绍+案例源码) - 9.小视频(SpringCache缓存)

搭建一个Java框架的移动端交友APP,涵盖登录、注册、动态、即时通讯、视频(算法推荐)、个人中心等功能; 技术选型: Spring Boot + SpringMVC + Mybatis + MybatisPlus + Dubbo,即时通讯选用环信即时通讯

yuan 2799

基于MATLAB/Simulink的四旋翼无人机PID控制与仿真(3)

《基于MATLAB/Simulink的四旋翼无人机PID控制与仿真》的第三篇,将为大家继续展示并完善控制与仿真模型。本文将在前文的基础上,完成控制环节增补、参数设置、测试等工作,在不考虑性能(低超调、快、准等)的前提下,保证系统对于一般输入(阶跃、斜坡)的响应稳定性。

jlx142857的博客 9321

Java 项目实战: 交友项目 - 小视频模块实战:访客记录、FastDFS 存储与 SpringCache 缓存

本文实现小视频模块核心功能:基于MongoDB记录“谁看过我”访客数据,使用FastDFS分布式文件系统存储视频与缩略图,通过Dubbo服务完成发布与列表接口。结合Spring Cache注解(@Cacheable/@CacheEvict)对视频列表缓存30秒,发布后自动清空缓存,提升性能并保证数据实时性。工程新增视频、访客实体及缓存配置,三条技术线(MongoDB、FastDFS、Redis)协同支撑完整链路。

Wang的专栏 326

2.1 amdgpu BO的类型和用途

AMDGPU驱动中,amdgpu_bo_create_user和amdgpu_bo_create_kernel分别用于创建用户空间和内核空间的buffer对象(BO)。用户空间BO支持元数据、tiling flags等属性,由GEM handle管理生命周期;内核BO则用于驱动内部资源如ringbuffer,自动pin并可选CPU映射。二者在返回类型、生命周期管理和使用场景上存在根本差异,前者面向图形渲染/AI等应用,后者服务于驱动内部任务。

图形/计算GPU的驱动与虚拟化技术分享园地 2208

【光子 AI 】应用 LangGraph 的核心模型和算法原理,完整实现一个极简版的 Agent 开发框架源代码,并给出具体的应用测试实际项目案例

下面给出一个“极简版 LangGraph 风格 Agent 框架”的完整可运行实现:用 有向图 + 状态(State) + 节点(Node) + 条件路由(Conditional Edge) + 循环(Loop) + Checkpoint 这一套核心模型/算法,把 Agent 组织成可编排、可测试的状态机工作流(这正是 LangGraph 的核心抽象之一:以 StateGraph 组织节点,节点读写共享状态,边做路由与循环,并支持持久化/回放)。 参考概念来源:LangGraph 官方文档对 “StateG

AI天才研究院 270

Java 项目实战: 交友项目 - 通用缓存SpringCache:介绍以及入门案例

本文介绍如何仅通过注解实现无代码缓存:SpringCache作为缓存抽象层,通过@EnableCaching开启,配合@Cacheable等注解,实现方法级缓存。三步即可上手:引入依赖、开启缓存、标注方法。默认使用内存缓存,可无缝切换为Redis。核心原理基于AOP代理,注意同类内部调用会导致注解失效。掌握注解区别(如@Cacheable与@CachePut)及SpEL表达式使用,避免常见坑点,实现高效缓存。

Wang的专栏 572

RK3588平台MIPI DSI屏幕调试记录

本文是基于RK3588平台,MIPI屏调试总结。调试过程中遇到的问题:一开始很久没亮,测量了电源电压和波形都正常。后来换了一组板子和屏幕做对比。最后是因为屏幕的座子没焊接好,二是接MIPI屏幕的排线太长会影响数据传输。换了一根短的线之后屏幕亮起。vp2vp3。

bingdund的博客 1389

Java 项目实战:交友项目 - 查询视频列表

小视频列表接口采用“推荐优先、时间兜底”策略:优先从Redis获取用户推荐视频ID串,按页码截取后批量查询MongoDB;若无推荐或已翻完,则按created倒序分页查询全量视频。通过page - redisPages实现页码无缝衔接,确保推荐与兜底区连续。接口返回带作者信息的VideoVo,关注/点赞状态暂置0,后续填充。整体设计兼顾性能与体验,实现高效信息流加载。

Wang的专栏 519

【科研建模】Pycaret自动机器学习框架使用流程及多分类项目实战案例详解

对pycaret的使用流程进行梳理,并详细介绍了基于多分类预测的案例

lys_828的博客 780

BVH2SMPL 实战笔记

BVH2SMPL 实战笔记

jacke121的专栏 843

Java 项目实战:交友项目 - SpringCache 入门案例深化:key 写法、RedisCacheManager 与写时失效

本文以交友项目视频列表缓存为例,解答 SpringCache 落地三大核心问题:key 生成防串号(通过 SpEL 组合 userId+page+pagesize)、Redis 过期时间与序列化配置(RedisCacheConfig 按 cacheName 设置 TTL,使用 GenericJackson2JsonRedisSerializer)、数据变更时缓存失效策略(发布视频时用 @CacheEvict(allEntries=true) 清除对应分组)。结合工程实读与踩坑验证,实现精准、可维护的缓存方案

Wang的专栏 733

060、模型蒸馏:把扩散模型压进单步的魔法

《端侧文生图模型蒸馏实战:从32步到1步的优化之道》摘要:针对边缘设备部署难题,本文分享了扩散模型蒸馏的核心经验。通过重构损失函数(感知损失+对抗训练+KL散度)、优化采样轨迹截取策略和改造学生模型架构,成功将Stable Diffusion压缩至单步推理。关键发现包括:小批量训练更有效、标签平滑保障生成多样性、双模型策略平衡质量与速度。在Cortex-A53芯片上实现300ms内生成,内存占用控制在200MB以下。特别强调工业部署需权衡压缩率与稳定性,建议保留迭代模式开关以适配不同场景需求。

2601_95651806的博客 150

探花交友_第9章_小视频方案(新版)

探花交友_第9章_小视频方案(新版) 1. 我的访客 1.1 需求分析 1.1.1 功能说明 1.1.2 数据库表 1.2 记录访客数据 tanhua-model tanhua-app-server tanhua-dubbo-interface tanhua-dubbo-mongo 1.3 首页谁看过我 需求分析 tanhua-model tanhua-app-server MovementController MovementService tanhua-dubbo-interface tanhua-du

无 1510

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

基 Spring Boot 2.x 构建的商城系统

阅读文本大概需要3分钟。作者: @城风 来源:https://juejin.im/post/5ca1b08fe51d455201119177本项目是和小伙伴开发的一套基...

天行健,君子以自强不息;地势坤,君子以厚德载物。 204

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

城风——基于SpringBoot2构建社区商城系统开源

本项目是和小伙伴开发的一套基于SpringBoot2的社区商城系统。主要是为了方便用户交友的同时拼单购物。本项目目前还属于雏形,但基本的业务已经实现了大概。基于该系统的前端采用Vue构建。同时兼顾Android和小程序端,前端展示正在开发过程中,后续会展出。 项目地址: github.com/fuyunwang/C… 项目的缘由 在当前互联网飞速发展的大潮下,人们的生活也变的更加便捷。本项目就...

weixin_33747129的博客 907

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer

本文系统梳理InnoDB内存结构中的ChangeBuffer、Adaptive Hash Index(AHI)与LogBuffer。ChangeBuffer缓存二级索引页不在内存时的变更,减少随机IO,仅在页加载时合并;聚簇索引修改立即执行,因数据本身不可延后。AHI基于高频等值查询自动构建哈希索引,加速定位,仅支持等值操作。LogBuffer为redo log内存缓冲,配合磁盘日志实现崩溃恢复。三者均服务于性能优化,其中ChangeBuffer与AHI共享BufferPool空间,而LogBuffer独立

bksczm的博客 282

Spring AI + GPT-5.5 + Ollama 配置教程:Base URL、超时与流式调用

由于不同 Spring AI、Spring Boot 小版本之间仍可能存在差异,创建项目时应检查 Spring Initializr、Spring AI BOM 和官方兼容矩阵,不要直接复制旧教程中的 Starter 名称和版本号。多人项目最好隔离不同客户端,并在脱敏前提下记录最终 URL、状态码、请求耗时、请求 ID 和模型 ID。Spring AI 接入模型 API,至少要同时确认 Starter、Base URL、请求路径、API Key、模型 ID、连接超时、读取超时和流式客户端这 8 项。

2601_95893075的博客 232

V8 机制、手写核心函数与 TypeScript 类型体操

或固定初始化(如数组先定长、对象先给全字段)保持类型与形状稳定;这些都比"换框架"更基础。只能处理纯数据(丢函数、Date、RegExp、循环引用报错)。生产级深拷贝要处理:原始值短路、循环引用、V8(Chrome / Node 的 JS 引擎)不是"解释一行跑一行"那么简单,它用。图:V8 执行流程——解释执行 + 热点优化,优化失败会 Deopt 回退。JS 内存自动管理,但"自动"不等于"不会漏"。是"固定间隔最多执行一次"。手写 Promise 是"懂异步"的试金石。是"停下后才执行最后一次",

qq_38468358的博客 346

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

要努力去发光,而不是被照亮~ 126

Spring Boot 数据源与 MyBatis(-Plus) 自定义配置详解

本文详解 Spring Boot 中单数据源环境下 HikariCP 与 MyBatis-Plus 的自定义配置。通过手动声明 SqlSessionFactory,实现下划线转驼峰、日志输出、一级缓存策略等精细化控制。结合 HikariCP 核心参数调优(如最大连接数、连接生命周期),确保高性能与稳定性。适用于仅需访问单一数据库的场景,避免多数据源复杂性,兼顾性能与可维护性。

BADAO_LIUMANG_QIZHI的博客 196

LeetCode Hot 100 二叉树专题(二)3 题(Java)

本篇文章共 3 题:543. 二叉树的直径、102. 二叉树的层序遍历、108. 将有序数组转换为二叉搜索树。 每道题都统一按「① 题目分析 & 解题思路 → ② 常见解法(附代码)及讲解 → ③ 知识点总结」的顺序组织;代码使用 Java 编写,节点定义沿用 LeetCode 默认的 TreeNode。

2401_88612756的博客 365

Decorator

TaskDecorator 是异步场景下"跨线程传递上下文"的标准方案,主要用于传递 MDC、用户信息、数据源路由等上下文数据,核心价值是让异步任务的日志链路不中断、业务上下文不丢失。TaskDecorator 是 Spring 提供的一个钩子接口,用于在异步任务执行前对 Runnable 进行包装,常用于跨线程传递上下文。如果你在项目中遇到具体的异步上下文传递问题,可以把代码贴出来,我帮你给出最合适的 Decorator 方案 👍。✅ 核心能力:在线程切换时,把"父线程的上下文"搬到"子线程"

wlwork66的博客 106

JDK 下载安装:装完 Java 提示「不是内部或外部命令」?环境变量没配

JDK 是写 Java 程序必须装的东西。它包含运行 Java 的虚拟机(JRE)和编译 Java 的编译器(javac)——装了它,能跑、.java文件能编译成可执行程序。免费,学习和小规模生产都不花钱。

科小怪的博客 630

C++ 多态详解:作用、原理与实战用法

多态是C++面向对象的核心特性,通过虚函数实现运行期动态绑定,使同一接口在不同对象上表现出不同行为。其核心作用包括统一接口、提升可扩展性、降低耦合、支持运行时分派与批量处理。实现依赖虚函数表(vtable)和虚指针(vptr),需注意虚析构函数、对象切片等陷阱。相比编译期多态(模板),运行期多态灵活性高但有调用开销。合理结合两者,可兼顾代码的灵活性与性能,是构建可扩展、低耦合系统的关键。

2402_85743155的博客 382

java框架大全

java框架大全

qq_39326137的博客 183
上一篇: Java 项目实战:交友项目视频功能——关注与取消关注的需求分析与代码实现
下一篇: Java 项目实战: 交友项目 - 通用缓存SpringCache:介绍以及入门案例
Wang's Blog
博客等级 码龄12年 7418粉丝 2407原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值