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

链接双端链表

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

后一篇:null

目录

[🔵 一、Java基础(Java 17)](#🔵 一、Java基础(Java 17))

答案:

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

答案:

[⚙️ 三、中间件](#⚙️ 三、中间件)

答案:

[🧠 四、JVM(JDK 11 G1 GC)](#🧠 四、JVM(JDK 11 G1 GC))

答案:

[⚡ 五、Java并发(Java 8)](#⚡ 五、Java并发(Java 8))

答案:

[🌱 六、Spring Cloud微服务](#🌱 六、Spring Cloud微服务)

答案:

[🤖 七、大模型与AI整合](#🤖 七、大模型与AI整合)

答案:


🔵 一、Java基础(Java 17)

题目

  1. 模式匹配陷阱

    当使用 instanceof 模式匹配时,if (obj instanceof String s && s.length() > 5)if (obj instanceof String s) 的字节码有何差异?解释其性能影响

  2. 记录类序列化

    如何通过自定义 JsonSerializer 解决 Jackson 序列化 Record 类时 componentN() 方法的命名冲突问题?

答案

题目1:模式匹配陷阱

java 复制代码
// 代码1:带条件的模式匹配
if (obj instanceof String s && s.length() > 5) 
→ 字节码:CHECKCAST + INVOKEVIRTUAL(length) + IF_ICMPLE

// 代码2:简单模式匹配
if (obj instanceof String s) 
→ 字节码:CHECKCAST + ASTORE(无额外检查)

性能影响

  • 条件组合会增加字节码指令(增加10-15%开销)

  • 复杂条件可能阻止JIT内联优化

题目2:记录类序列化

java 复制代码
public class RecordSerializer extends JsonSerializer<MyRecord> {
    @Override
    public void serialize(MyRecord value, JsonGenerator gen, SerializerProvider provider) {
        gen.writeStartObject();
        // 手动序列化字段(避开componentN()冲突)
        gen.writeNumberField("id", value.id());
        gen.writeStringField("name", value.name());
        gen.writeEndObject();
    }
}

// 注册序列化器
ObjectMapper mapper = new ObjectMapper()
    .registerModule(new SimpleModule().addSerializer(MyRecord.class, new RecordSerializer()));

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

题目

  1. 索引跳跃扫描优化

    联合索引 (shop_id, status)WHERE status = 1 查询中触发 Using index for skip scan 的条件是什么?给出执行计划分析

  2. 死锁自动处理

    如何通过 innodb_deadlock_detectinnodb_lock_wait_timeout 实现死锁自动回退?对比两种机制的适用场景

答案

题目1:索引跳跃扫描优化
触发条件

  1. 联合索引前导列(shop_id)基数低(如<10个离散值)

  2. 查询条件不包含前导列(仅用status)

  3. 优化器成本估算低于全表扫描

执行计划

sql 复制代码
EXPLAIN SELECT * FROM orders WHERE status = 1;
-- 输出:Extra = 'Using index for skip scan'

题目2:死锁自动处理
答案

机制 原理 适用场景
innodb_deadlock_detect=ON 主动检测死锁并回滚代价最小事务 高频写场景(默认开启)
innodb_lock_wait_timeout=50 等待超时后自动回退 对实时性要求低的系统

生产建议

sql 复制代码
# my.cnf配置
innodb_deadlock_detect = ON  # 死锁检测
innodb_lock_wait_timeout = 30  # 超时30秒回退

⚙️ 三、中间件

a) Redis 6.2
题目

设计秒杀库存扣减:如何用 Redis Lua 脚本实现 DECRBY + EXPIRE 的原子操作?处理超卖问题的三种方案对比

b) Kafka 3.5
题目

如何通过 ConsumerRebalanceListener 实现分区再平衡时的 消费位移自动迁移 ?解释 __consumer_offsets 的底层存储结构

答案

a) Redis秒杀设计

Lua 复制代码
-- Lua脚本原子操作
local key = KEYS[1]
local decrAmount = tonumber(ARGV[1])
local expireSec = tonumber(ARGV[2])

local stock = redis.call('GET', key)
if not stock or tonumber(stock) < decrAmount then
    return 0  -- 库存不足
end

redis.call('DECRBY', key, decrAmount)
redis.call('EXPIRE', key, expireSec)  -- 续期
return 1

超卖解决方案对比

方案 优点 缺点
Lua原子操作 零超卖,高性能 Redis单点风险
分布式锁+DB校验 绝对精确 性能低(<1000 TPS)
Redis队列预扣减 高吞吐 存在少卖问题

b) Kafka位移迁移
答案

java 复制代码
consumer.subscribe(topics, new ConsumerRebalanceListener() {
    @Override
    public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
        // 提交当前位移(确保不丢失)
        consumer.commitSync(); 
    }

    @Override
    public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
        // 从外部存储加载位移(如MySQL)
        Map<TopicPartition, Long> offsets = loadOffsetsFromDB(partitions);
        partitions.forEach(tp -> consumer.seek(tp, offsets.getOrDefault(tp, 0L)));
    }
});

__consumer_offsets存储结构

  • 内部Topic(50分区)

  • Key: group+topic+partition

  • Value: offset+metadata+timestamp


🧠 四、JVM(JDK 11 G1 GC)

题目

  1. G1调优实战

    针对 32GB 堆的订单系统,如何设置 -XX:G1HeapRegionSize-XX:MaxGCPauseMillis 平衡吞吐量与延迟?给出容器环境推荐值

  2. 元空间溢出急救

    给出 Metaspace OOM 时快速定位类加载器泄漏的 jcmd 命令组合

答案

题目1:G1调优实战

bash 复制代码
# 32GB堆容器环境推荐配置:
-XX:+UseG1GC
-XX:G1HeapRegionSize=16M  # 提升大对象分配效率
-XX:MaxGCPauseMillis=200  # 目标暂停时间
-XX:InitiatingHeapOccupancyPercent=40  # 更早启动并发标记
-XX:G1ReservePercent=15   # 避免晋升失败

平衡效果

  • 吞吐量 > 92%

  • GC暂停 ≤ 200ms

题目2:元空间溢出急救
答案

bash 复制代码
# 1. 查看类加载器统计
jcmd <pid> VM.classloader_stats

# 2. 检查未卸载的代理类
jcmd <pid> GC.class_histogram | grep 'Proxy'

# 3. 生成直方图快速定位
jmap -histo:live <pid> | grep 'ClassLoader'

⚡ 五、Java并发(Java 8)

题目

  1. 线程池资源耗尽

    分析 ThreadPoolExecutorqueueCapacity=Integer.MAX_VALUE 时导致OOM的四种场景,给出饱和策略最优选型

  2. 读写锁升级限制

    为何 ReentrantReadWriteLock 不允许读锁升级为写锁?通过源码分析其死锁风险

答案

题目1:线程池OOM预防
四种OOM场景

  1. 任务无限堆积(队列过大)

  2. 线程泄漏(未回收异常线程)

  3. 任务对象持有大内存

  4. maximumPoolSize 设置过高

饱和策略选型

java 复制代码
new ThreadPoolExecutor(
    10, 100,
    60L, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(1000),  // 限制队列大小
    new ThreadPoolExecutor.CallerRunsPolicy() // 避免OOM
);

题目2:读写锁升级限制

java 复制代码
// 读锁升级写锁(错误示例)
readLock.lock();
try {
    writeLock.lock();  // 此处永久阻塞(死锁风险)
} finally {
    readLock.unlock();
}

死锁风险

  • 多个读锁试图升级时互相阻塞

  • 源码中Sync类明确禁止升级:

    java 复制代码
    protected final boolean tryAcquire(int acquires) {
        if (getReadHoldCount() != 0)  // 持有读锁时禁止获取写锁
            throw new IllegalMonitorStateException();
        // ...
    }

🌱 六、Spring Cloud微服务

题目

  1. 网关动态路由

    如何通过 Spring Cloud GatewayRouteLocatorBuilder 实现基于Nacos配置中心的 实时路由更新

  2. 安全加固实战

    设计OAuth2.1资源服务器:如何用 JwtAuthenticationConverterscope 声明转换为 GrantedAuthority

  3. 分布式配置

    Spring Cloud Config 无法刷新 @Value 注解时,如何通过 @RefreshScope + ContextRefresher 实现批量热更新?

答案

题目1:网关动态路由

java 复制代码
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder, NacosConfigManager config) {
    return builder.routes()
        .route(r -> r.path("/product/**")
            .uri(config.getConfig("gateway.routes.product-service"))  // 动态URI
        .route(r -> r.path("/order/**")
            .filters(f -> f.filter(new GrayReleaseFilter()))  // 灰度权重过滤
            .uri("lb://order-service")
        .build();
}

题目2:安全转换器

java 复制代码
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
    JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
    converter.setAuthorityPrefix("SCOPE_"); 
    converter.setAuthoritiesClaimName("scope");  // 解析scope字段
    
    return new JwtAuthenticationConverter() {
        @Override
        protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
            return converter.convert(jwt);
        }
    };
}

题目3:配置热更新

java 复制代码
@RefreshScope
@Component
public class DynamicConfig {
    @Value("${app.threshold}")
    private int threshold;  // 动态刷新字段
}

// 手动触发批量刷新
@Autowired private ContextRefresher refresher;
public void refreshAll() {
    refresher.refresh();  // 刷新所有@RefreshScope Bean
}

🤖 七、大模型与AI整合

题目

  1. 流式响应优化

    如何通过 Spring WebFluxSseEmitter 实现大模型生成结果的实时流式传输?给出背压处理方案

  2. 提示词注入防御

    设计 @ControllerAdvice 全局过滤器:如何通过正则表达式拦截 system: 指令的越权访问?

  3. 成本控制体系

    如何通过 Bucket4j + Micrometer 监控大模型API的Token消耗量?实现日预算$100自动熔断

答案

题目1:流式响应优化

java 复制代码
@GetMapping("/ai/stream")
public SseEmitter streamResponse(@RequestParam String prompt) {
    SseEmitter emitter = new SseEmitter(30_000L);
    aiClient.generateStream(prompt, chunk -> {
        try {
            emitter.send(chunk);  // 流式发送
        } catch (IOException e) {
            emitter.completeWithError(e);
        }
    });
    return emitter;
}

背压处理 :通过SseEmitter超时设置(30秒)自动断开慢客户端

题目2:提示词注入防御

java 复制代码
@ControllerAdvice
public class PromptAdvice extends ResponseEntityExceptionHandler {
    @ExceptionHandler(InvalidPromptException.class)
    public ResponseEntity<Object> handlePromptInjection(InvalidPromptException ex) {
        return ResponseEntity.status(400).body("非法指令");
    }
}

@ModelAttribute
public void checkPrompt(@RequestBody String body) {
    if (body.contains("system:") || body.contains("sudo")) {
        throw new InvalidPromptException();
    }
}

题目3:成本控制体系

java 复制代码
// Token消耗计量器
Counter tokenCounter = Metrics.counter("openai.token.cost");

public String callModel(String prompt) {
    int tokens = estimateTokens(prompt);
    // 预算检查($100 = 100,000 Token)
    if (tokenCounter.count() > 100_000) {
        throw new BudgetExceededException();
    }
    tokenCounter.increment(tokens);
    return aiClient.generate(prompt);
}

熔断配置

bash 复制代码
management:
  metrics:
    export:
      prometheus:
        enabled: true
  endpoint:
    prometheus:
      enabled: true