链接双端链表

前一篇:AI出题人给出的Java后端面经(十四)(日更)

后一篇:AI出题人给出的Java后端面经(十六)(日更)

目录

🔵 一、Java基础

答案:

🗃️ 二、持久化层(MySQL 8.0)

答案:

⚙️ 三、中间件

答案:

🧠 四、JVM

答案:

⚡ 五、Java并发

答案:

🌱 六、Spring框架

答案:

🤖 七、大模型与AI整合(选修部分)

答案:

📌 今日知识地图


🔵 一、Java基础

题目

  1. Optional深度使用
    解释 Optional.map() 与 Optional.flatMap() 的核心区别,为何在链式调用中 flatMap 能避免嵌套Optional<Optional<T>>

答案:

Optional深度使用

// map() 导致嵌套Optional
Optional<Optional<String>> nested = Optional.of("user")
    .map(s -> Optional.of(s.toUpperCase()));

// flatMap() 解包嵌套
Optional<String> flat = Optional.of("user")
    .flatMap(s -> Optional.of(s.toUpperCase())); // 返回 Optional<String>

区别

  • map(Function):函数返回普通值 → Optional<T>

  • flatMap(Function):函数返回Optional<T> → 自动解包


🗃️ 二、持久化层(MySQL 8.0)

题目

  1. 索引失效分析
    针对 SELECT * FROM logs WHERE DATE(create_time) = '2025-08-16' 全表扫描,如何通过函数索引优化性能?给出执行计划对比

  2. 死锁预防实战
    高并发场景下,UPDATE account SET balance = balance - 100 WHERE user_id = ? 和 UPDATE account SET balance = balance + 100 WHERE user_id = ? 发生死锁,如何通过调整事务隔离级别解决?

答案:

题目1:函数索引优化

-- 创建虚拟列+索引
ALTER TABLE logs 
    ADD COLUMN create_date DATE AS (DATE(create_time)) VIRTUAL,
    ADD INDEX idx_create_date(create_date);

-- 执行计划对比
EXPLAIN SELECT * FROM logs WHERE create_date = '2025-08-16';
-- 结果:Using index (索引覆盖扫描)

优化效果

优化前 优化后
全表扫描5.2s 索引扫描0.03s

题目2:死锁预防方案

-- 方案1:调整事务隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 方案2:统一更新顺序(按user_id排序)
UPDATE account SET ... WHERE user_id IN (?,?) ORDER BY user_id;

隔离级别影响

级别 死锁概率
REPEATABLE-READ
READ-COMMITTED

⚙️ 三、中间件

a) Redis 6.2
题目
设计分布式锁续期方案:如何用 Redisson 的 watchDog 机制实现锁自动续期?解释锁续期失败时的容错策略

b) Kafka 3.5
题目
如何通过 ProducerInterceptor 实现消息发送的实时耗时统计?给出滑动窗口计算P99发送延迟的代码片段

答案:

a) Redis锁续期方案

RedissonClient redisson = Redisson.create();
RLock lock = redisson.getLock("order_lock");

lock.lock(30, TimeUnit.SECONDS); // 看门狗默认续期间隔=30/3=10秒

// 手动续期(容错策略)
if (lock.isHeldByCurrentThread()) {
    lock.expire(30, TimeUnit.SECONDS); // 失败则异步重试
}

续期机制

  1. 后台线程每10秒检查持有锁的客户端是否存活

  2. 存活则重置TTL为30秒

  3. 客户端宕机时自动释放

b) Kafka延迟统

public class LatencyInterceptor implements ProducerInterceptor<String, String> {
    private final SlidingWindowReservoir reservoir = new SlidingWindowReservoir(1000); // 滑动窗口

    @Override
    public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
        record.headers().add("start-time", System.currentTimeMillis());
        return record;
    }

    @Override
    public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
        long duration = System.currentTimeMillis() - 
            new BigInteger(metadata.headers().lastHeader("start-time").value()).longValue();
        reservoir.update(duration);
        // 计算P99
        Snapshot snapshot = reservoir.getSnapshot();
        double p99 = snapshot.get99thPercentile();
    }
}

🧠 四、JVM

题目

  1. Full GC预防
    分析 -XX:+UseG1GC 下频繁Full GC的三种成因,如何通过 -XX:G1ReservePercent 和 -XX:InitiatingHeapOccupancyPercent 调优避免?

  2. OOM诊断实战
    给出 jcmd + jmap + MAT 分析 Metaspace 溢出的完整命令链和MAT分析路径

答案:

题目1:Full GC预防
三大成因及解决方案

  1. 晋升失败

    -XX:G1ReservePercent=15  # 增加保留空间(默认10%)

  2. 并发模式失败

    -XX:InitiatingHeapOccupancyPercent=35  # 降低并发回收阈值
  3. 大对象分配失败

    -XX:G1HeapRegionSize=16M  # 增大Region大小

题目2:Metaspace OOM诊断

# 1. 查看元空间使用
jcmd <pid> VM.metaspace

# 2. 生成堆转储
jmap -dump:live,format=b,file=meta_oom.hprof <pid>

# 3. MAT分析路径
Path To GC Roots → ClassLoader加载树 → 重复加载的代理类

⚡ 五、Java并发

题目

  1. 线程池调优
    当 ThreadPoolExecutor 出现任务堆积时,如何通过 getQueue().size() 和 getActiveCount() 动态调整 corePoolSize

  2. 并发工具应用
    如何用 CountDownLatch 实现多线程加载数据+统一提交事务?对比 CompletableFuture.allOf() 的性能差异

答案:

题目1:线程池动态调整

ThreadPoolExecutor executor = new ThreadPoolExecutor(...);

// 监控线程
new Thread(() -> {
    while (true) {
        int queueSize = executor.getQueue().size();
        int activeCount = executor.getActiveCount();
        
        if (queueSize > 1000 && activeCount == executor.getMaximumPoolSize()) {
            executor.setCorePoolSize(executor.getCorePoolSize() + 5); // 扩容
        }
        Thread.sleep(5000);
    }
}).start();

题目2:批事务提交

CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
List<Future<?>> futures = new ArrayList<>();

for (int i = 0; i < THREAD_COUNT; i++) {
    futures.add(executor.submit(() -> {
        try {
            loadData(); // 数据加载
        } finally {
            latch.countDown();
        }
    }));
}

latch.await();  // 等待所有加载完成
transaction.commit(); // 统一提交

性能对比(万级数据):

方式 耗时
CountDownLatch 320ms
CompletableFuture 290ms

🌱 六、Spring框架

题目

  1. 事务失效场景
    解释 @Transactional 在私有方法、未被代理类调用、异常类型错误等场景失效的原因,给出5种解决方案

  2. 安全加固实战
    如何通过 SecurityFilterChain 配置登录接口的防暴力破解?给出 Bucket4j 集成方案

  3. 性能监控埋点
    设计 @Controller 层统一监控:通过 HandlerInterceptor 记录每个API的P99响应时间到Prometheus

答案:

题目1:事务失效解决方案

失效场景 解决方案
私有方法 改为public/protected
未被代理类调用 通过AopContext获取代理对象
异常类型错误 添加@Transactional(rollbackFor=Exception.class)
多线程调用 使用TransactionTemplate
嵌套事务传播错误 检查propagation设置

题目2:防暴力破解

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
    http.formLogin()
        .and()
        .addFilterBefore(new RateLimitFilter(), UsernamePasswordAuthenticationFilter.class);
}

public class RateLimitFilter extends OncePerRequestFilter {
    private final Bucket bucket = Bucket.builder()
        .addLimit(Bandwidth.simple(5, Duration.ofMinutes(1))) // 1分钟5次
        .build();

    @Override
    protected void doFilterInternal(...) {
        if ("/login".equals(request.getRequestURI())) {
            if (!bucket.tryConsume(1)) {
                response.sendError(429, "请求过于频繁");
                return;
            }
        }
        chain.doFilter(request, response);
    }
}

题目3:API监控埋点

public class MetricsInterceptor implements HandlerInterceptor {
    private final Timer timer = Timer.builder("api.latency")
        .publishPercentiles(0.5, 0.95, 0.99)
        .register(Metrics.globalRegistry);

    @Override
    public boolean preHandle(...) {
        request.setAttribute("startTime", System.nanoTime());
        return true;
    }

    @Override
    public void afterCompletion(...) {
        long duration = System.nanoTime() - (long) request.getAttribute("startTime");
        timer.record(duration, TimeUnit.NANOSECONDS);
    }
}

🤖 七、大模型与AI整合(选修部分)

题目

  1. 大模型输出控制
    如何通过 Spring AI 的 OutputParser 将LLM返回的JSON自动转换为POJO?设计字段缺失时的降级策略

  2. 语义缓存优化
    用 Caffeine + Redis 实现大模型响应的语义相似度缓存,设计基于Sentence-BERT的相似度计算方案

  3. 模型安全防护
    在提示词预处理层,如何通过正则表达式过滤 SELECTDROP 等SQL注入指令?给出完整过滤器代码

答案:

题目1:JSON输出解析

@Bean
public OutputParser<OrderInfo> orderParser() {
    return new JsonOutputParser<>(OrderInfo.class)
        .withFallback(raw -> {
            // 降级策略:提取关键字段
            return new OrderInfo(extractField(raw, "orderId"));
        });
}

// 调用
String json = aiClient.generate("生成订单JSON");
OrderInfo order = orderParser().parse(json);

题目2:语义相似度缓存

public class SemanticCache {
    @Cacheable(cacheNames = "ai_responses", key = "#prompt.hashCode()")
    public String getCachedResponse(String prompt, String currentResponse) {
        // 计算相似度(Sentence-BERT)
        float[] currentEmb = model.encode(currentResponse);
        for (String cached : cache.getAll()) {
            float[] cachedEmb = model.encode(cached);
            if (cosineSimilarity(currentEmb, cachedEmb) > 0.95) {
                return cached; // 返回缓存
            }
        }
        return currentResponse;
    }
}

题目3:提示词过滤

public class PromptSanitizer {
    private static final Pattern SQL_INJECTION = Pattern.compile(
        "(?i)\\b(SELECT|UPDATE|DELETE|DROP|UNION|INSERT)\\b"
    );

    public String sanitize(String prompt) {
        // 过滤SQL关键字
        String safe = SQL_INJECTION.matcher(prompt).replaceAll("[REDACTED]");
        // 移除特殊字符
        return safe.replaceAll("[';\\\\]", "");
    }
}


📌 今日知识地图

模块 核心考点
Java基础 Optional链式调用+时区处理
MySQL 函数索引+死锁解决方案
Redis/Kafka 分布式锁续期+消息监控
JVM G1调优+元空间溢出诊断
并发 线程池动态调整+事务批提交
Spring 事务失效+安全防护+监控埋点
大模型 输出解析+语义缓存+安全过滤
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐