
年初集中面了几家头部互联网公司的Java后端岗位从简历初筛到三轮技术面下来最直观的感受是面试官的提问习惯已经从“Spring Boot怎么用”变成了“你如何理解Spring Boot的自动装配它和你项目里的分布式系统有什么关系”。这篇实录是我把从Spring Boot单体应用到分布式微服务技术栈的真实问答按场景整理成的一份复盘笔记。里面没有标准答案式的背诵更多是我在面试中被追问、被挑战后的思考过程适合正在准备Java进阶面试、或者想从单体转向微服务方向的同学参考。1. 面试前的技术地图Java基础与Spring Boot的准备思路1.1 Java基础考点怎么筛背什么、不用背什么先说结论现在大厂Java面试对基础知识的考查很少直接问“String和StringBuilder的区别”这种入门题而是会用一连串追问逼你把知识串起来。我准备的时候把精力主要放在三块集合框架、并发工具、JVM内存模型。集合框架里HashMap是绝对的高频点。面试官一般会从“HashMap的底层结构”切入然后一路问到put流程、哈希冲突怎么解决、链表什么时候转红黑树、为什么阈值是8、扩容时为什么要重哈希。我建议自己动手画一遍put流程先算hash定位桶位置如果桶里是链表就尾插链表长度达到8且数组长度超过64就树化否则先扩容。这一串讲下来基本能覆盖十分钟的考察。并发这块Java八股文里最常被拎出来的是synchronized、volatile和CAS。我自己的记忆方式是抓核心矛盾可见性、原子性、有序性。volatile解决可见性和有序性但它不解决原子性synchronized通过Monitor锁同时解决三个问题CAS通过Unsafe的compareAndSwap实现无锁更新但会有ABA和自旋开销的问题。面试官如果追问“AtomicInteger是怎么保证线程安全的”其实就是想听UnsafeCASVolatile这一条线。提示准备Java基础时不要只记结论。建议准备一个自己能完整复述的“三分钟讲解”比如“从new一个对象到发生Full GC的完整过程”能串起JVM内存划分、对象创建流程、GC Roots、垃圾回收算法。这个题我几乎每次面试都会被问到。1.2 Spring Boot知识体系怎么搭从使用到原理Spring Boot的知识面不建议按API一个一个背而是按“一个请求从端口进来到返回响应中间发生了哪些事”这条主线来梳理。顺着这条线你能自然引出DispatcherServlet、HandlerMapping、Controller、拦截器、异常处理、参数解析这些模块也能解释为什么Spring Boot一个main方法就能把Web应用跑起来。我把Spring Boot的学习拆成了五个层级。第一层是会用写starter、写配置、打jar包第二层是理解自动装配摸清SpringBootApplication和EnableAutoConfiguration背后的机制第三层是理解生命周期包括SpringApplication.run()的启动阶段和Bean的创建销毁过程第四层是外部化配置搞清楚不同配置来源的优先级第五层是运维治理Actuator、监控、优雅下线这些线上要用的能力。这场面试让我印象最深的是面试官没有直接考Spring Boot原理而是给我看了一段公司内部的starter代码问“这段代码为什么放在META-INF/spring/路径下”。如果只是停留在会用层面那一刻很容易卡住。实际上Spring Boot 2.7之后自动装配信息已经不再写在spring.factories而是放在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件里。这种变化是面试中很有区分度的知识点准备时要特别留意版本差异。2. Spring Boot核心场景问答题解从启动流程到线上监控2.1 自动装配原理被追问最多的一环这个题我至少被问到五次。核心要讲清楚一件事为什么我引入一个starter的jar包不用写任何配置就能获得对应的功能。我的讲解方法是拆三步。第一步SpringBootApplication是一个组合注解包含SpringBootConfiguration、EnableAutoConfiguration和ComponentScan。第二步EnableAutoConfiguration通过Import(AutoConfigurationImportSelector.class)引入了自动配置的加载逻辑这个Selector会读取classpath下的自动配置文件。第三步读取到的每个自动配置类都是带有ConditionalOnClass、ConditionalOnMissingBean之类条件注解的类Spring会根据当前classpath里有没有对应的依赖、用户有没有自定义Bean来决定是否加载这个配置。比如Redis的场景。引入spring-boot-starter-data-redis之后classpath里有了RedisTemplate等类RedisAutoConfiguration上的ConditionalOnClass(RedisOperations.class)条件成立于是Spring自动创建一个连接工厂和RedisTemplate。如果我在自己的配置类里也定义了一个RedisTemplate那么ConditionalOnMissingBean就会让自动配置里的Bean失效以我的自定义Bean为准。这套机制的本质是“约定优于配置”。面试官听完一般会追问一句自动配置给你的维护带来过什么坑这里我踩过一次自动配置的RedisTemplate默认使用的序列化器是JdkSerializationRedisSerializer如果直接用它缓存对象Redis里都是乱码样式的二进制内容。后来我项目里都是显式自定义一个GenericJackson2JsonRedisSerializer的RedisTemplate。这个细节既说明你理解原理也说明你真正处理过生产问题。注意Spring Boot 2.7前读取自动配置类的文件是META-INF/spring.factories2.7之后改为META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。面试时主动提到这个版本差异会明显加分。2.2 启动流程与Bean生命周期从main方法到服务可用面试官问“Spring Boot启动过程”时我建议回答时把逻辑链落在SpringApplication.run()上。我的标准答案是启动流程会先做运行环境准备判断应用类型Servlet还是Reactive、读取配置、准备Environment然后创建并刷新ApplicationContext在refresh阶段完成BeanDefinition的加载、BeanFactoryPostProcessor的执行、Bean的实例化与初始化最后调用ApplicationRunner或CommandLineRunner执行用户自定义逻辑并启动内嵌的Tomcat。这里容易被追问的是Bean的生命周期回调。我习惯把整个生命周期讲成一条线BeanDefinition加载后先由BeanFactoryPostProcessor做配置修改实例化之前由InstantiationAwareBeanPostProcessor拦截对象创建后执行属性填充接着走Aware接口回调然后是BeanPostProcessor的postProcessBeforeInitialization、PostConstruct、InitializingBean的afterPropertiesSet最后是postProcessAfterInitialization。销毁阶段对应的是PreDestroy和DisposableBean。我第一次面大厂时因为只回答到“Spring创建Bean然后加载”面试官当场反问“那事务注解是什么时候被解析的”这是一个非常好的组合题。答案其实是EnableTransactionManagement会注册一个BeanPostProcessor具体来说是BeanFactoryTransactionAttributeSourceAdvisor和TransactionInterceptor在Bean初始化后阶段为符合条件的方法生成代理对象。所以如果你被问“Spring AOP和Bean生命周期有什么关系”本质上就是BeanPostProcessor在初始化前后做代理增强。2.3 多环境配置与外部化配置优先级一个真实翻车案例Spring Boot的配置看似简单但面试很容易被问到“配置文件加载顺序”。我准备了一个自己踩过的坑来展开之前上线前在服务器上设置了SPRING_PROFILES_ACTIVEprod的环境变量但应用启动后读到的却是application.yml里的默认端口我一度以为配置没生效。后来排查才发现Spring Boot的外部化配置有严格的优先级顺序。大体上命令行参数优先级最高其次是Java系统属性System.getProperties然后是操作系统环境变量再往下才是jar包外的application-{profile}.yml、jar包内的application-{profile}.yml、application.yml。我当时的端口配置写在application.yml里而另一个配置源没有显式覆盖它所以看起来像“没生效”。这个知识点面试官通常会换一种方式考给你四个配置源让你说出最终生效的值。准备时可以记住一个小口诀命令行 系统属性 OS环境变量 profile配置 默认配置。另外Spring Boot 2.4之后引入的spring.config.import机制也经常被追问它让多配置文件的管理方式有了新变化需要单独了解。2.4 监控治理盘问Spring Boot Admin与Actuator的实战组合热词里有句“spring boot实现监控都有哪些需求和功能”这恰好是我被问到的一道开放题。当时面试官问我如果线上一个服务CPU飙升但你没有进服务器你第一件事做什么这个问题其实考察的是监控体系能不能支撑最快的定位。我当时的回答是Spring Boot的监控需求可以从三个维度拆健康状态、运行指标、日志与告警。健康状态由/actuator/health提供包含数据库连接、Redis连接、磁盘空间等指示器的检查结果运行指标通过/actuator/metrics暴露JVM内存、GC次数、线程状态、HTTP请求耗时等再加上/actuator/loggers可以动态调整日志级别线上无需重启就能排查问题。Spring Boot Admin的价值则是把这些端点聚合成一个可视化管理界面可以看堆内存趋势、线程数、环境变量还能直接切换日志级别。我在项目里实践过配置非常简单服务端引入spring-boot-admin-starter-server并开启EnableAdminServer客户端引入spring-boot-admin-starter-client然后指定服务端地址和暴露所有端点。对比一下监控需求Actuator原生方案Spring Boot Admin增强健康检查/actuator/health可视化的应用状态列表JVM指标/actuator/metrics内存、GC、线程图表日志调整/actuator/loggers页面直接修改日志级别告警通知无内置订阅事件触发通知我当时做监控的时候忽略了一个重要配置客户端必须设置management.endpoints.web.exposure.include*否则大部分端点默认不暴露Admin页面里很多数据都是空的。这种细节就是面试里可讲的“实战经验”。3. 分布式微服务场景技术全栈问答3.1 注册中心的选型博弈Nacos、Eureka与ZooKeeper怎么选微服务场景下注册中心是第一个会被问到的组件。面试官常见的问法是你的项目里为什么选Nacos而不是Eureka这背后考察的是你对CAP和集群场景的理解。我当时的思考是这样的Eureka的设计遵循AP原则强调可用性和分区容错性各节点之间通过心跳互相复制任何一个节点宕掉都不影响服务列表的读取但可能短暂读到不一致的列表ZooKeeper遵循CP原则强调一致性主节点挂掉时会触发重新选举选举期间整个服务列表可能暂时不可用Nacos则比较灵活临时实例走AP模式和Eureka类似服务端不持久化实例信息持久化实例走CP模式还能直接做配置中心所以国内项目里Nacos更常见。面试中还有一个高频追问服务消费者拿到注册中心返回的服务列表后如果某个服务提供方已经宕机消费者多久会感知到这个问题要分两层回答。第一层是服务提供方与注册中心之间的心跳Nacos临时实例默认5秒发一次心跳15秒没收到就标记不健康30秒剔除第二层是消费者侧的本地缓存与定时刷新Nacos通过UDP或HTTP轮询推送变更但消费者本地仍有缓存所以极端情况下请求还是会打到已经不健康的实例上。正因如此调用侧还需要配置重试和熔断来兜底。经验面试时被问“Nacos和Eureka的区别”不要只背“AP还是CP”的结论最好能结合自己项目说明白“我遇到过服务下线但调用方依然报错”的场景然后解释注册中心为什么不可能是最终一致性解决方案的唯一答案。3.2 网关层的职责从路由转发到流量管控网关是我项目里的核心组件。Spring Cloud Gateway是我选择的方案它基于WebFlux和Reactor非阻塞模型对比Zuul 1.x的Servlet同步模型在性能上更有优势。面试官几乎一定会问网关做了哪些事我按职责拆成四个层次。第一是路由转发根据Path、Host或Header断言把请求路由到对应的下游服务第二是过滤器链可以在请求到达下游之前做鉴权、加解密、灰度标识注入也可以在响应返回前做统一包装第三是限流用RequestBodyLimiter配合Redis做令牌桶限流第四是跨域与安全统一处理CORS配置、防重放参数校验。比较容易被追问的点是“网关和普通Spring Boot应用在请求处理上有什么不同”。答案的关键在于Gateway的过滤器分为全局过滤器和GatewayFilter这些过滤器和WebFlux的HandlerMapping有严格的关系而它不是部署在Servlet容器里所以很多基于Servlet的Filter相关经验在网关层不能直接复用。另外GlobalFilter中做耗时统计时要小心不要在pre阶段直接sleep否则会阻塞Netty事件循环线程导致整个网关吞吐下降。3.3 服务间调用与超时重试纠缠OpenFeign的真实配置服务间调用这一节面试官非常喜欢深挖OpenFeign的超时、重试和熔断配置。我第一家大厂面试就栽在这里原因是只会说“用Feign调用其他服务”但被问到“调用超时了会发生什么”时答得模棱两可。OpenFeign默认的底层HTTP客户端在早期版本是Ribbon负责负载均衡超时也由Ribbon配置控制配合Spring Cloud LoadBalancer之后超时策略会有所不同。生产上我通常会在调用方设置连接超时和读取超时比如连接超时2000ms、读取超时5000ms并根据下游接口的P99耗时来决定是否允许重试。这里最关键的坑是重试必须谨慎如果下游接口不是幂等的比如下单、扣款重试可能导致重复扣款。如果面试官继续追问“Feign和RestTemplate的对比”我会回答Feign是声明式HTTP客户端通过接口加注解定义配合服务发现自动获取服务列表RestTemplate更灵活但需要写更多代码。底层两者都依赖HTTP客户端和负载均衡组件。那一刻如果能顺手说出“Feign在Spring Cloud Alibaba中默认集成Netty或OkHttp可以通过feign.httpclient.enabled切换”会让回答显得更有层次。3.4 分布式事务Seata之外先讲清楚为什么需要它分布式事务在网购类项目里几乎是必问题。面试官经常这样开场“你的订单服务和库存服务是分开的两个库你怎么保证扣库存和创建订单同时成功”如果只回答“用MQ消息队列最终一致性”还谈不上完整因为要处理消息发送失败、消息重复消费、订单状态回查这些细节。我参考项目里用的是Seata AT模式但面试时更推荐先讲清楚分布式事务的几种方案再落到选型。XA协议是数据库层的强一致性资源锁定时间长高并发场景下会有性能瓶颈TCC模式通过Try、Confirm、Cancel三个方法实现柔性事务性能好但需要业务代码实现三阶段方法侵入性强Saga模式把一个长事务拆成多个子事务中间出现异常就反向补偿AT模式则通过全局事务管理器记录SQL执行前后的数据镜像利用undo_log表做回滚业务代码几乎无侵入。AT模式虽然好用但面试官经常追问它的局限。我会指出两点第一性能取决于全局锁粒度跨服务调用时间过长会拉长事务边界第二不支持多数据源跨数据库的复杂SQL。所以我最后的建议是核心交易链路用TCC或可靠消息简单跨库场景才用AT模式。在面试里能主动说出方案的适用边界比单纯回答“用了Seata”更有含金量。3.5 配置中心与链路追踪把排查效率拉满分布式场景里配置管理和排查链路是离不开的两个话题。配置中心方面我用的是Nacos Config核心场景是配置动态刷新数据库连接池参数、限流阈值、开关类配置变更后不需要重启服务。这里有一个值得准备的细节就是RefreshScope的原理它是通过在Bean上套一层Scope代理来实现的配置变更时销毁旧代理、重新创建Bean。如果没有这个注解修改配置后Bean里的值不会被刷新。链路追踪这一块面试会问“一个请求从网关到订单服务再到积分服务怎么快速定位问题在哪个环节”。我项目里通过Spring Cloud Sleuth配合Zipkin或者SkyWalking来实现核心概念是TraceId和SpanId每经过一个微服务Trace服务的TraceId不变并生成新的Span。生产排查慢请求时看到日志里的TraceId就能把链路日志串起来再结合各个Span的耗时定位瓶颈到底在哪个服务。如果让面试官看到你部署过SkyWalking可以主动提一下它是基于字节码增强做探针的对业务代码零侵入Java Agent的方式在启动参数里加上-javaagent即可。这比单纯说“集成过”更有说服力。4. 高频系统设计题与场景问答实录4.1 秒杀系统如果只让你用一台MySQL你怎么接住一万QPS系统设计题是大厂面试中拉开差距的关键。最常见的是秒杀场景面试官一般会追问“你设计的方案能不能在没有中间件支持的情况下也能保护数据库”这其实是考察你对削峰、限流、缓存的理解深度。我的答题框架是提前把请求挡在前面。首先在接入层用网关做限流比如令牌桶算法每秒只放行部分用户请求到业务层其次在应用层用本地缓存如Caffeine存储秒杀商品的库存快照和活动状态大部分读请求直接命中本地内存不查Redis也不查MySQL真正生成订单时才去Redis用Lua脚本预扣减库存扣减成功再异步发送MQ消息最后库存扣减和订单创建在消费者侧执行数据库层用乐观锁防止超卖。这里要重点讲解为什么用Lua脚本而不是先GET再SET减库存因为Redis的“读库存、判断库存足够、扣减”三步不是原子的并发情况下会出现超卖。而Lua脚本能保证整个操作在Redis单线程内一次执行。面试官如果追问“数据一致性怎么保证”我回答Redis预扣减是准入控制最终以MySQL实际扣减为准若MQ消费失败则通过定时任务回补库存并把订单标记为失败这是最终一致性方案。4.2 分布式锁从Redis到ZooKeeper不只setnx这么简单分布式锁几乎每场面试都会遇到。最初级的回答是“用Redis的SETNX加锁”但面试官随便一追问就会暴露问题“如果加锁后服务宕机了锁怎么释放”正确思路是给锁设置过期时间同时考虑业务执行超过锁过期时间的问题。Redisson的解决方式比较经典通过一个看门狗机制默认30秒锁过期如果业务还没执行完看门狗会定期续期避免锁在业务执行中被释放。相比之下ZooKeeper分布式锁用临时顺序节点实现客户端创建临时有序节点判断自己是否是最小的那个节点如果是就获得锁不是则监听前一个节点客户端异常断开后临时节点自动消失天然没有死锁问题。我在面试中还总结过一个对比表格方便快速理清思路方案原理优点需要警惕的坑Redis SETNXLua原子设置加过期时间性能高、实现简单主从切换可能丢锁业务超时需续期Redisson看门狗自动续期锁安全性高、支持公平锁需要额外维护锁续期机制ZooKeeper临时顺序节点强一致、无死锁性能不如Redis会话失效影响数据库唯一约束插入唯一记录实现最简单性能和数据库压力问题这个题有一个很好的加分回答点分布式锁不只是互斥工具还要考虑锁粒度。比如秒杀场景可以只锁用户维度而不是锁整个商品ID否则同一商品的所有用户请求都会串行化吞吐上不去。4.3 缓存一致性先更新数据库还是先删缓存只要项目里有Redis面试官几乎一定会追问缓存和数据库的一致性。我的回答从“Cache Aside旁路缓存”模式讲起读的时候先读缓存没有则读数据库再回填缓存写的时候有两种做法先更新库再删缓存或者先删缓存再更新库。直接说结论我推荐“先更新数据库再删除缓存”。因为先删缓存容易出现一个窗口期在缓存还没回填之前另一个线程的旧数据把缓存写回了导致后续读到的都是脏数据。反过来先更新库再删缓存虽然极端情况下删除缓存失败会让缓存中留旧数据但可以通过延迟双删来兜底也就是更新库之后先删一次缓存过几百毫秒再删一次保证并发场景下旧缓存被覆盖的概率降到最低。如果继续追问“为什么更新库存不直接更新缓存”我会回答缓存本质上不是数据源而是加速层。直接把写请求打到Redis一旦缓存和数据库之间出现同步失败就会长期脏读。用“更新库删缓存”的方式下次读请求会重新回填缓存永远从数据库获取最新值这比试图维护“缓存与库永远一致”要实用得多。5. 面试中容易被追问的细节与避坑实战5.1 排查思路的连招CPU飙升和接口超时面试进行到后半程面试官喜欢给一个线上真实问题“服务没有报错但接口突然很慢你怎么办”这个问题不是考你会用哪个工具而是考排查节奏。我的回答顺序是先看监控大盘确认是单机问题还是集群问题如果是单机问题就登录机器看CPU、内存、磁盘IO。CPU飙升时用top -Hp查看线程CPU占用用jstack导出线程栈把线程ID转成十六进制找到对应线程看是业务代码死循环、频繁GC还是锁竞争。内存不足和频繁Full GC时用jstat -gcutil观察GC次数和耗时再用jmap -histo和jmap -dump分析堆对象。如果现场不允许重启可以用Arthas的dashboard、thread和sc命令动态排查Arthas在面试里提出来很加分。接口慢的排查思路也类似但更看重分层网络层先看建立连接是否耗时应用层用Arthas Trace命令跟踪方法耗时数据层看慢SQL和连接池是否打满。我习惯加一句“链路追踪的TraceId是我最重要的排查入口否则在几十个微服务里找一次慢请求会淹死在日志海里。”这句话往往能让面试官点头。5.2 我踩过的两次典型翻车现场面试中的“翻车”不一定是知识不足很多时候是表达策略出了问题。我第一场大厂面试时面试官问“Spring Boot的starter是如何被加载的”我直接开始背AutoConfigurationImportSelector源码细节结果忘了先讲清楚版本差异和条件装配的整体逻辑。面试官反馈是“你知道的很多但组织得乱”。后来我把任何原理性问题的回答都固定成“是什么、为什么引入、具体机制、适用边界”四段式表达清晰多了。第二次翻车发生在分布式事务题。我上来就说“项目里用的Seata”但面试官紧接着问“AT模式回滚失败怎么处理”。我一时只想到靠全局事务编排回答得很苍白。复盘后我意识到AT模式依赖undolog做回滚如果第二阶段回滚失败需要定时任务扫描全局事务日志做重试或告警人工介入。这个场景想清楚之后再遇到同类问题就能答得比较从容。提示大厂面试的问题经常是一环扣一环的连环追问。准备时试着问自己“如果我答完这句下一个会被问什么”把每个知识点往下多预演两层。往往第二、第三个追问才是真正筛人的地方。5.3 项目深挖环节如何讲好自己的真实项目现在的Java后端面试几乎必问“挑一个你最有成就感的项目介绍”。这个环节最容易出现的错误是堆功能列表“我的项目有订单、支付、优惠券、后台管理。”面试官完全无感。我后来的做法是只挑一个核心场景用问题、方案、难点、收益四步讲完。比如我讲过一个商城项目核心问题是“订单创建高峰期数据库连接池被打满”方案是引入Redis预扣减库存和MQ异步削峰难点是Redis与MySQL库存的一致性收益是秒杀高峰期订单数据库连接占用从95%降到40%。这个描述里特别有价值的是“尽快说出量化数值”面试官追问的技术点也会顺着你准备好的方向走。另外有一个强烈建议讲到项目里的技术组件时不要只报名字至少要能画出来一张部署拓扑图哪个服务调用哪个服务、数据流向是什么。很多面试官听着听着直接在白板上画你项目里的服务拓扑让你标出哪里可能出问题。这种题答好了基本等于锁定offer。6. 从面试官视角复盘那些我没答好的问题6.1 问完“你有什么想问的”之前我一般做什么面试结尾的“你有什么想问我的”表面上是客套实际上是你最后一次展示思考深度和了解团队的机会。我通常不会问放假、加班而是问“团队目前最头疼的技术难点是什么”如果对方说“服务拆分之后分布式事务处理比较痛苦”我就能顺势结合之前聊的Seata方案再补充两句把前面的技术讨论收尾。我也准备过一个反问清单用来判断团队的技术成熟度你们当前的微服务规模有多大注册中心和配置中心自建还是买的云服务线上出问题时观测告警体系能不能做到分钟级定位有没有做灰度发布和回滚自动化。这些问题既体现我对一线工程落地的关注也帮自己判断这家公司值不值得去。6.2 三轮面试节奏的差异基础面、场景面、系统设计面大厂Java后端面试通常分三到四轮。第一轮侧重基础Spring Boot自动装配、JVM、并发、MySQL是重灾区这一轮只要准备扎实问题不大。第二轮侧重项目和场景面试官会让你讲项目细节然后不断追问边界条件比如“你这个方案在高并发下有什么问题”“如果Redis挂了怎么办”。第三轮偏向系统设计给的题目往往很开放比如“设计一个IM系统”“设计一个秒杀系统”考察的是拆解问题、权衡取舍的表达。三轮面试我最大的体会是高手不是在背标准答案而是有一套自己的“决策树”。比如提到Redis他脑子里自动弹出“缓存穿透、击穿、雪崩如何应对”“持久化RDB/AOF怎么选”“分布式锁怎么实现”然后根据面试官的追问选择展开多少。这套决策树是通过反复复盘真实项目练出来的不是一天突击能补上的。面了这么多轮之后我个人最深的体会是面试官真正想验证的不是你会不会用某个注解而是你在对方接连追问到底的时候还能不能保持逻辑在线。准备阶段花时间把Spring Boot的启动和自动装配路径完整走一遍把Nacos、Feign、Redis这些组件在自己项目里的超时、重试、降级配置逐一对照捋一遍性价比远高于闷头刷三百道题。最后分享一个小技巧每次面试结束把面试官的连环追问当作一份最好的复习提纲记下来对照查漏下一场你会明显感觉更稳。