途虎2023秋招Java笔试试卷B核心考点解析与避坑指南

功能测试面试问题 1、介绍一下你们常用的测试用例设计方法 我们常用的测试用例设计方法是等价类、边界值,因果图和错误推断等。最常用的还是等价类划分和边界值法,等价类的划分可以让我们更全面的覆盖功能需求,避免遗漏,也能让我们用尽量少的测试用例来达到最好的测试效果,一般会划分有效等价类和无效等价类,有效等价类来自需求的描述,无效等价类还可以根据不同的角度再次划分,比如空,超长,不符合输入类型,特殊字符等,然后在每个等价类中分别提取部分取值去设计测试用例。 边界值的使用主要是因为在等价类的边界部... 阅读详情

这两年秋招笔试我是真没少刷,各大厂的卷子基本都见过一遍。途虎养车这套2023秋招Java笔试试卷B,在牛客和脉脉上被讨论的频次不低。它不算那种硬核到劝退的题库,但胜在覆盖面完整:Java基础、JVM、并发、Spring生态、MySQL、Redis、算法编程,甚至还有结合业务场景的设计题,几乎把互联网公司后端Java岗的核心考点全部囊括进去了。

不管你是准备投途虎,还是想拿这套卷子当模拟题练手,我都建议认真过一遍。这篇文章不打算逐题贴答案,而是把试卷B背后真正想要考察的能力模型拆给你看,顺便把答题时容易踩的坑、准备时容易被忽视的点都梳理出来,希望能帮你少走弯路。

1. 试卷B的定位:途虎秋招笔试到底想筛什么样的人

1.1 途虎的业务形态决定了考点方向

先聊一个很多人忽略的问题:为什么这套卷子的考点长这样?

途虎养车本质上是汽车后市场的在线服务平台,线上有商城、预约、会员系统,线下有大量直营和加盟门店,背后还连着一整套供应链、库存、订单、支付、物流体系。这种业务形态决定了它招的Java工程师,不是去写底层框架或中间件的,而是做业务系统的——也就是大量订单流、库存流、会员流的高并发读写、数据一致性、缓存设计、消息异步处理。

所以你看试卷B,它不会考你怎么实现一个JVM、怎么从零写一个RPC框架,而是重点考察这些内容:

  • Java基础是否扎实,能不能写出健壮的代码;
  • 有没有处理过并发、内存这类真实生产问题;
  • 对Spring Boot这类主流框架的原理理解到不到位;
  • 会不会用MySQL索引、事务、Redis缓存解决实际业务问题;
  • 能不能在白板/在线编辑器里快速写出一道中等难度的算法题。

说得直白点,这套卷子筛选的是"能干活、能扛事、基础没硬伤"的后台业务开发,而不是纯粹的算法竞赛选手或者框架调包侠。

1.2 题量、题型与分值结构参考

据我拿到试卷B的同学反馈和自己的刷题印象,这套卷子的题型大致是:

题型 大致占比 考察内容
单选题/多选题 25%-35% Java语法、集合、异常、面向对象概念
判断题 5%-10% 容易混淆的细节知识点
简答题 15%-20% JVM内存、垃圾回收、线程池、数据库事务等
SQL/数据库题 10%-15% 索引设计、SQL编写、事务隔离级别
编程题 20%左右 排序、链表、字符串处理、中等难度LeetCode
场景设计题 10%左右 订单超时、库存扣减、缓存雪崩等业务问题

这个结构有一个很现实的意义:它决定你答不完也能过。笔试不是要求你拿满分,而是考察你在有限时间里的取舍能力。选择题、判断题那种一眼能出的题,尽量快速拿下;简答题答到点子上、条理清楚;编程题至少完整AC一道;场景题把思路写清楚,哪怕没写完整代码也有分。

顺便说一句,过了笔试之后,这个成绩会同步到后续面试官那边的评估表里。笔试里暴露出来的薄弱点,非常大概率会在一面二面中被追问。所以笔试不只是为了"过",更是为了摸清自己的短板再针对性准备。

提示:试卷B既然是B卷,说明还有A卷甚至C卷。不同考场的题目并不相同,但考点覆盖会被有意对齐。所以刷B卷的价值不是背原题,而是覆盖考点。

2. Java基础题:集合、异常、语言特性的踩分点

2.1 HashMap和ConcurrentHashMap,必考中的必考

在试卷B的选择题和简答题里,HashMap相关的题目基本没有缺席过。它几乎是最能区分"背过八股"和"真正理解"的知识点。

第一个高频考点:HashMap的数据结构。JDK 8之后是数组加链表加红黑树。当链表的长度超过8,并且数组长度达到64时,链表会转成红黑树,目的是把最坏情况下的查找复杂度从O(n)降到O(logn)。但树化不是常态,节点数减少到6以下时又会退化成链表,避免红黑树的自旋在数据量小时反而浪费性能。

第二个高频考点:put操作的完整流程。计算hash时先对key的hashCode做一次高16位与低16位的异或扰动,目的是让高位也参与到数组下标的计算中,减少冲突。然后用(n-1)&hash计算桶下标,JDK 8的公式不用取模运算,而是用位运算,前提是数组长度必须是2的幂。如果发生哈希冲突,就尾插法追加到链表尾部(注意JDK 7是头插法,头插法在并发扩容时可能形成环,这就是HashMap线程不安全的一个典型原因)。当元素数量超过阈值loadFactor*capacity时,触发扩容,默认负载因子0.75,容量翻倍。

第三个高频考点:为什么HashMap线程不安全。你只需要记住两个场景:JDK 7并发扩容可能成环,导致get死循环;JDK 8虽然改成尾插法解决了成环问题,但多线程put时可能出现数据覆盖,因为put是check-then-act操作,多个线程同时判断某个桶为空,然后同时赋值,后写的会覆盖先写的。

ConcurrentHashMap则是另一个必考考点。JDK 7用Segment分段锁,继承ReentrantLock,理论上支持Segment数组大小的并发度。JDK 8放弃分段锁,改用CAS加synchronized锁住桶的头节点,并发粒度更细。它不允许key或value为null,这一点和HashMap不同,原因是为了避免并发场景下的二义性:如果get返回null,你没法判断是key不存在还是value本身是null,在并发环境下这种不确定性是有隐患的。

2.2 异常体系与try-with-resources

异常相关的题在试卷B里通常是选择题,但有一类比较坑:给你一段代码,问输出什么,或者问finally里的代码是否一定执行。

先理清基本概念。Java的异常是Throwable的两个分支:Error和Exception。Error是JVM层面的严重错误,比如OutOfMemoryError、StackOverflowError,应用程序不应该去捕获。Exception下面又分受检异常和非受检异常。受检异常必须显式捕获或向上抛,典型的是IOException、SQLException;非受检异常继承RuntimeException,可以不做处理,典型的是NullPointerException、ArrayIndexOutOfBoundsException、ClassCastException。试卷里经常让你区分一个异常到底属于哪一类。

关于finally,有一道经典题的答案你必须背下来:finally块中的代码并不是一定执行。以下情况不执行:

  • 在try或catch中调用了System.exit()终止JVM;
  • JVM崩溃或断电等极端情况;
  • 在try块中没有正常执行到finally之前,比如陷入了死循环。

另一种考察方式是finally中的return会覆盖try中的return。如果try里先返回一个值,finally里又有一个return,最终返回的是finally里的值。这个细节很容易被忽略,我建议你最好自己写个例子跑一遍,比死记结论要牢靠得多。

try-with-resources是JDK 7引入的资源自动关闭机制,凡是实现了AutoCloseable接口的类,都可以放在try后面的圆括号里,代码块执行完后资源会被自动关闭。它比传统的手动finally关闭更安全,因为即使代码块内抛异常,关闭资源时如果也抛异常,原始的异常会被保留。笔试时如果出现资源关闭相关的代码,优先考虑用try-with-resources。

2.3 Lambda、Stream与枚举的冷门考点

Lambda和Stream在试卷B里不会考得太深,但经常以高频选择的形式出现。有一个容易错的知识点:Lambda表达式实际上是对函数式接口的实例化。所谓函数式接口,就是只包含一个抽象方法的接口,比如Runnable、Comparator、Callable,以及java.util.function包下的Function、Predicate、Supplier、Consumer。判断一个接口是不是函数式接口,可以看它有没有加@FunctionalInterface注解,默认方法、静态方法不会破坏函数式接口的定义。

Comparator.comparing这个用法在笔试编程题和简答题里出现过。它的核心逻辑是让你指定一个提取key的函数,然后按照key对元素排序,比如 list.sort(Comparator.comparing(User::getAge)) 。如果你想把某个值排到最前面,可以用 Comparator.comparing(User::getAge).thenComparing(...) 来实现多重排序规则,或者用一些技巧把特殊的key映射为优先值。这里提醒一点:排序如果是倒序,注意null值的处理,Java 8的Comparator默认对null不友好,需要用到Comparator.nullsLast来处理。

枚举也是高频考点。它可以定义字段、构造方法、抽象方法,可以配合switch使用。有一个经典问题是:为什么枚举能实现单例?因为枚举类的构造器是私有的,且JVM层面保证了每个枚举常量只会被实例化一次,同时天然支持序列化,不会因为反序列化创建新的实例。这一点比双重检查锁定的单例实现更安全,笔试问"最推荐的单例写法"时,答案就是枚举单例。

Stream的常见操作里,map、filter、collect、sorted是必会的基础。容易被考到的是peek与map的区别:peek是中间操作,接收Consumer,不改变元素,而map是转换操作。还有一个点是parallelStream,它不是万能加速器,对于共享可变状态的操作反而可能出错,笔试如果问性能问题,一定要提到线程安全因素的考量。

3. JVM与并发:内存溢出、GC与线程安全的组合拳

3.1 各种OOM的成因区分,别只会说内存不够

关于JVM内存结构,试卷B的简答题几乎必考。你需要把运行时数据区说得清清楚楚:堆、虚拟机栈、本地方法栈、程序计数器、方法区(JDK 8后是元空间)。其中堆是对象分配的主要区域,虚拟机栈是执行Java方法时创建栈帧,程序计数器是当前线程执行的字节码行号指示器。

试卷里经常出现一个很具体的报错,比如最原始的题目可能是给你一段OutOfMemoryError相关日志,问这个错误是什么原因。常见的OOM可以分成这么几类:

报错信息 发生区域 主要原因
Java heap space 堆 对象太多或对象过大,堆内存不足
GC overhead limit exceeded 堆 GC频繁执行但回收效果差,JVM自我保护
Metaspace 元空间 加载的类过多或动态生成类膨胀
unable to create new native thread 操作系统层面 线程数超过系统上限
insufficient memory 本地内存 操作系统无法提供足够的内存给JVM

特别注意"insufficient memory"这个报错。它不是常见的堆内存溢出,而是JVM向操作系统申请本地内存失败。什么叫本地内存?比如JVM需要分配线程栈、DirectByteBuffer对应的堆外内存、JIT编译器相关的内存、元空间,这些都算。如果你看到"java: OutOfMemoryError: insufficient memory",优先怀疑几个方向:物理内存本身不够、进程的虚拟内存地址空间耗尽、cgroup或容器限制导致的内存配额不足、以及换页空间不足。

排查这类问题的思路:先 free -m 看物理内存,再 top 看JVM进程内存占用, jmap -heap 看堆内情况, jstat -gcutil 看GC压力。如果堆内存使用率不高但进程整体内存持续上涨,就要怀疑堆外内存泄漏,比如DirectByteBuffer没有释放。我见过一个网关服务频繁OOM的案例,最后定位到是因为堆外缓存使用了大量DirectMemory,又没有注意回收,导致操作系统层面的内存被耗尽。

3.2 线程池的核心参数与执行流程

线程池的考察在途虎这类笔试中基本是必考的,形式可能是简答题也可能是选择题。你至少要能准确说出ThreadPoolExecutor的7个核心参数,外加解释阿里的规范"不要用Executors创建线程池"。

7个参数:corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。流程是这样的:

  1. 提交任务后,如果当前线程数小于核心线程数,创建新线程执行任务;
  2. 如果线程数大于等于核心线程数,任务先进入阻塞队列;
  3. 如果队列满了,继续创建线程,直到达到最大线程数;
  4. 如果线程数已经达到最大值,就触发拒绝策略。

这里要强调的是队列选择对业务的影响。LinkedBlockingQueue无界队列会导致最大线程数形同虚设,任务无限在队列里堆积;SynchronousQueue不缓存任务,来一个任务就必须创建一个新线程,适合任务量小但要求快速响应的场景;ArrayBlockingQueue有界队列是最常用的。另一个细节:corePoolSize和maximumPoolSize之间的线程,在空闲时间超过keepAliveTime后会被回收。

4种拒绝策略:AbortPolicy直接抛异常(默认)、CallerRunsPolicy由调用线程执行、DiscardPolicy丢弃任务、DiscardOldestPolicy丢弃队列中最旧的任务。如果在途虎这种秒杀活动场景下设计线程池,建议用有界队列加CallerRunsPolicy,因为抛异常会导致接口直接失败,而由调用线程执行可以起到降级和限流的效果。

阿里的规范为什么要禁掉Executors?两个原因。Executors.newFixedThreadPool用的是无界的LinkedBlockingQueue,任务堆积可能导致内存溢出;newCachedThreadPool的maximumPoolSize是Integer.MAX_VALUE,队列是SynchronousQueue,并发量大时会创建大量线程,同样会造成OOM或者线程资源耗尽。所以生产环境必须手动new ThreadPoolExecutor,把参数暴露出来进行可控配比。

3.3 synchronized、ReentrantLock、volatile与ThreadLocal

并发编程部分的考点相对密集。synchronized和ReentrantLock的区别,是每一家公司的笔试题里都有的常客。你能答出两者都是可重入的、都可以保证可见性和原子性,已经很不错了。但答出以下差异就更加分:

  • synchronized是隐式锁,自动获取和释放;ReentrantLock是显式锁,需要lock和unlock,且unlock必须放在finally中。
  • ReentrantLock支持公平锁和非公平锁,synchronized只有非公平锁。
  • ReentrantLock可以中断等待、可以设置超时(tryLock),synchronized不能。
  • ReentrantLock支持多个Condition,synchronized只有一个监视器锁。
  • JDK 6之后synchronized引入了偏向锁、轻量级锁、重量级锁的升级过程,所以性能不见得比ReentrantLock差,在低竞争环境下synchronized表现更优。

volatile是一个高频考点。它保证两点:可见性和有序性(禁止指令重排序),但不保证原子性。典型例子是"volatile修饰的计数器在多线程下i++仍然不安全",因为i++是读取-修改-写入三步操作。在场景设计题里,你如果提出用volatile保证并发安全,面试官很可能追问原子性问题,这是一个容易暴露深浅的点。

ThreadLocal的考点主要是两个:每个线程都有独立的变量副本,以及它的内存泄漏问题。ThreadLocalMap的Entry继承了WeakReference,key是弱引用,但value是强引用。如果ThreadLocal对象没有被显式remove,而线程仍然存活,那么key可以被回收但value无法被回收,接下来key变成null,value却一直存在,导致内存泄漏。正确的做法是在finally块里调用remove。在线程池场景下使用ThreadLocal尤其危险,因为线程会被复用,一个请求设置的值可能在另一个请求中被读到,这就是跨请求数据串扰,必须在使用完立即清理。

死锁相关问题在试卷里一般是问你"如何避免死锁"。回答思路是:破坏四个必要条件之一——互斥(无法破坏,锁本身就是为了互斥)、持有并等待(可以采用一次性申请所有锁)、不可剥夺(可以设置超时,tryLock)、循环等待(可以保证加锁顺序一致)。笔试写代码时,最实用的手段就是所有线程都按照同一个顺序获取多个锁。

4. 框架与中间件:业务系统开发能力的试金石

4.1 Spring Boot自动配置与MyBatis防注入

试卷B在框架题上侧重的是主流实用型的考察,Spring Boot的自动配置原理是我的建议重点。你要能说清楚:Spring Boot通过 @EnableAutoConfiguration 注解,导入AutoConfigurationImportSelector,利用SpringFactoriesLoader从 META-INF/spring.factories 或 AutoConfiguration.imports 文件中加载所有候选的自动配置类,再通过 @ConditionalOnClass 、 @ConditionalOnMissingBean 这类条件注解决定哪些配置生效。简而言之,自动配置就是"根据classpath下的依赖和配置属性智能地组装Bean"。

MyBatis有一个经典问题: #{} 和 ${} 的区别。前者是预编译占位符,传入的值会被当作参数传给PreparedStatement,由JDBC驱动进行转义,能够有效防止SQL注入;后者是字符串直接拼接,相当于把变量内容直接拼进SQL语句里,存在注入风险。凡是涉及order by、表名这类无法使用参数占位符的场景,必须对传入内容做白名单校验,更不应该直接把用户可控的字符串拼进SQL。

除此以外,MyBatis一级缓存是SqlSession级别的,默认开启;二级缓存是namespace级别的,需要显式配置。在分布式环境下,二级缓存可能出现数据一致性问题,所以并没有无脑开启为佳。这些细节很可能出现在选择题或简答题的加分项里。

4.2 MySQL索引与事务隔离级别

MySQL在途虎这类重度依赖数据库存储的系统中是核心中的核心。订单要查、门店要查、库存要查、用户要查,几乎所有业务都离不开查询,所以索引相关题目必考。

先理清B+树索引的特性:它是多路平衡搜索树,非叶子节点只存索引键,叶子节点存全量数据(聚簇索引)或主键值(二级索引)。这种结构天然适合范围查询、排序操作,因为叶子节点之间用双向链表相连,一次范围扫描不需要回根节点反复查找。

聚簇索引和非聚簇索引的区别是:InnoDB的主键索引就是聚簇索引,数据行物理存储在叶子节点;二级索引的叶子节点存储的是主键值,所以通过二级索引查找数据时需要"回表"。这时候覆盖索引就能派上用场:如果查询的列恰好都在二级索引里,就不需要回表,直接返回索引数据,性能提升很明显。最左前缀原则说的是联合索引的匹配规则,比如建立(a,b,c)联合索引,查询条件里有a和b能用到索引,只有b和c就用不上。

事务隔离级别这块,四个级别你必须背得滚瓜烂熟:读未提交、读已提交、可重复读、串行化。InnoDB默认是可重复读。由此引入三个概念:脏读(读到未提交数据)、不可重复读(同一查询条件下,第二次读到的数据被其他事务修改了,但字段值一样)、幻读(第二次读到的行数变多了)。MVCC机制用undo log版本链和readView实现一致性快照读,使得在可重复读级别下,同一个事务内多次查询能看到一致的数据快照。

笔试常见的坑是:可重复读级别下,普通快照读不会出现幻读,但当前读(select ... for update、update、delete)仍可能发生幻读。所以InnoDB引入了间隙锁和next-key lock来配合解决,但这块在笔试里只要点到即可,面试时才需要详细展开。

4.3 Redis缓存与分布式锁的答题思路

Redis相关题在试卷B里的存在感很强,尤其是缓存穿透、缓存击穿、缓存雪崩这三个概念,几乎是简答和场景题的钉子户。

  • 缓存穿透:查询一个不存在的数据,缓存里没有,请求一直打到数据库。解决思路:缓存空值,为不存在的数据设置一个较短的过期时间;或者使用布隆过滤器,在缓存之前先判断key是否存在。
  • 缓存击穿:某个热点key过期,大量并发请求同时去打数据库。解决思路:互斥锁,让同一时刻只有一个线程去重建缓存;或者逻辑过期,让热点key的逻辑过期时间短暂延长,后台异步刷新。
  • 缓存雪崩:大量key同时失效,或者Redis宕机,导致数据库被打爆。解决思路:过期时间加随机值,避免key在同一时间过期;做多级缓存;Redis集群高可用方案;限流降级。

分布式锁怎么答?一句话概括:用Redis的SETNX加EXPIRE原子命令,或者用Redisson看门狗机制实现可续期的锁。注意一个关键细节:不能分两步执行SETNX和EXPIRE,否则进程在设置锁之后、设置过期时间之前挂了,锁就永远不会释放。应该是 SET lockKey value NX PX 30000 ,一条命令完成。释放锁时要用Lua脚本比较value是否一致再删除,避免误删其他线程的锁。这个场景在途虎的营销活动、优惠券系统、订单状态更新中非常常见。

4.4 API安全对接与ES异步写入的实务延伸

从热搜词的观察来看,Java Spring Boot的API Key安全对接、ES异步写入这类偏实战的问题,也很常出现在面试追问里。

API Key的通用方案是:客户端调用服务端接口时,header中带上AppId和Key,服务端用拦截器(HandlerInterceptor)或过滤器(OncePerRequestFilter)校验。更安全的做法是加签名:把业务参数和时间戳拼接成字符串,用约定密钥做HMAC-SHA256签名,服务端用同样的密钥重放签名并比对,同一时间戳短时间内允许重复请求且过期时间校验通过,就能防止重放攻击。

ES的异步写入在Java里通常配合消息队列来实现:业务数据先写入MySQL,同时发送一条消息到Kafka/RabbitMQ,消费者从MQ拉取消息后异步写入ES,这样既不影响主链路时延,也能在ES写入失败后重试。一旦写入失败重试次数达到上限,可以丢进死信队列人工排查。这套思路在途虎这种既有结构化订单数据、又有大量日志/搜索需求(门店搜索、商品搜索)的场景里是很典型的通用方案。

5. 算法编程题:排序、链表与场景设计的实战节奏

5.1 排序算法的边界条件与复杂度

试卷B的编程题难度大概在LeetCode中等偏下,不会出特别变态的题,但这不意味着你可以轻视。最常见的排序题就是冒泡排序和快速排序。

冒泡排序虽然简单,但笔试时有个优化点:某一轮遍历如果没有任何交换,说明数组已经有序,可以直接退出。加上这个标志位,在接近有序的数组上可以降到接近O(n)的时间复杂度,这是区分你有没有真实写过代码的细节。

快速排序是另一个高频考点。它基于分治:选一个基准元素,把数组分成小于基准和大于基准两个部分,再对子数组递归排序。最坏情况发生在每次基准都选到最大值或最小值时,时间复杂度退化为O(n²),所以实际应用中常用三数取中法或者随机选取基准来避免这种情况。平均时间复杂度O(nlogn)。

我贴一个笔试风格的快排实现,注意边界条件:

public void quickSort(int[] arr, int left, int right) {
    if (left >= right) return;
    int pivot = partition(arr, left, right);
    quickSort(arr, left, pivot - 1);
    quickSort(arr, pivot + 1, right);
}

private int partition(int[] arr, int left, int right) {
    int pivotValue = arr[left];
    int i = left, j = right;
    while (i < j) {
        while (i < j && arr[j] >= pivotValue) j--;
        arr[i] = arr[j];
        while (i < j && arr[i] <= pivotValue) i++;
        arr[j] = arr[i];
    }
    arr[i] = pivotValue;
    return i;
}

笔试里如果只要求排序,先考虑Arrays.sort()能不能用;如果要求手写排序,快速排序和归并排序选一个练熟。归并排序还额外适合解决逆序对问题,必要时可以用。

5.2 链表和二分查找的代码注意事项

链表相关的题在试卷里出现过多次,最常见的是反转链表、环形链表检测、删除倒数第N个节点。

反转链表用迭代法写最稳:三个指针,prev、curr、next,每次把curr.next指向prev,然后整体后移。边界条件是链表为空或只有一个节点。代码量很小,但很多人会在最后忘记把头节点指向正确位置,建议先在纸上画一遍指针变换。

环形链表检测的标准解法是快慢指针:fast每次走两步,slow每次走一步,如果存在环,两者必然相遇;如果fast走到null,说明无环。这题的证明关键是:环入口之前的路程加上环内步数存在模运算关系,笔试不会让你严格证明,但你要能表达清楚"快慢指针在环内一定相遇"。

二分查找是笔试最常见的"看似容易但容易写错"的题目,核心是循环条件 left <= right 以及每次收缩区间时 mid 的更新。经典写法:

int binarySearch(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + ((right - left) >> 1);
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;
}

注意 mid = left + ((right - left) >> 1) 这个写法,它可以防止left+right整数溢出,笔试和面试中提到这一点会显得你考虑周全。还有就是边界条件的取值:如果循环是 left < right ,通常配合的是区间左闭右开,容易搞混,建议全篇统一用 <= +左闭右闭,减少心智负担。

5.3 结合业务的场景设计题怎么答

试卷B的场景设计题非常务实,几乎都是围绕互联网业务系统的常见问题。比如:

  • 如何设计一个订单超时自动关闭系统?
  • 如何设计一个秒杀场景下的库存扣减方案?
  • 途虎预约到店场景,怎么避免同一个时间段被重复预约?

回答这类题有一个通用框架:先明确核心诉求,再说数据存储选型和关键流程,最后补充异常和并发处理。

以订单超时关闭为例。最简单的方案是起一个定时任务,每隔一段时间扫描数据库中超时的订单,批量更新状态。这个方案的优点是实现简单,缺点是扫描范围大、对数据库有压力,而且存在时间窗口误差。更优的方案是用延迟队列:订单创建时把订单ID放到延时队列,延迟时间到后,消费者取出订单并判断是否已支付,如果未支付则关闭订单。可以用RabbitMQ的延迟消息插件,也可以用Redis的过期key配合监听,或者直接用Redisson的延迟队列。答题时提到"用延迟队列替代定时轮询"这个优化方向,通常能拿到不错的分数。

库存扣减场景要注意两个点:一是防止超卖,二是防止数据库压力过大。超卖的经典解法是条件更新: UPDATE stock SET count = count - 1 WHERE goodsId = ? AND count > 0 ,减少的话影响行数为0就说明库存不足。如果要缓解数据库压力,可以先用Redis的incr/decr原子操作预扣减库存,再异步同步到数据库。答题时把这两层讲清楚,就比只写"加乐观锁"要翔实得多。

预约时间冲突的场景,本质是区间重叠判断。数据库层面可以在门店、时间段字段上建联合唯一索引,也可以采用"提前锁定时间段+事务内检查+唯一约束"的双保险设计。笔试时能把思路说清楚,比写出完整代码更得分。

6. 笔试现场避坑:环境配置、编译错误与时间分配

6.1 本地环境与在线编译器的经典报错

笔试过程中很多人不是挂在题目上,而是挂在环境上,这一点经常被忽略。几个实测概率很高的报错值得提前说道说道。

Lombok相关报错:编译时提示 java: You aren't using a compiler supported by lombok, so lombok will not work 。这个报错通常在JDK版本和Lombok版本不匹配的时候出现。比如你本地用的JDK 21,但项目依赖的Lombok还是1.16系列。解决方案:升级Lombok版本到1.18.30以上,或者降低JDK版本。在笔试在线环境中大概率不会遇到,但如果用本地IDE写练习题,这类问题很常见,提前把环境调好能省下大把时间。

另一个高频报错: java: 警告: 源发行版 17 需要目标发行版 17 (或者反过来,目标发行版低于源发行版)。这说明编译器使用的JDK版本和项目target版本不一致。在IDEA里需要检查File - Project Structure - Project SDK和Modules里的Language Level,在Maven项目里则需要检查pom.xml中maven.compiler.source和maven.compiler.target是否一致。最好在 <properties> 里统一配置:

<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
</properties>

这样Maven在编译时会沿用指定的源码和目标版本,避免IDE默认配置覆盖引起混乱。

还有就是数组越界异常,我记得热搜词里也有这个。写代码时很多越界都发生在循环边界上,比如 for (int i = 0; i <= arr.length; i++) ,应该用 i < arr.length 。笔试时尽量在循环条件里统一写成 < ,并且对数组访问前做空数组判断。这种边界错误在在线判题系统里会直接导致运行时错误,AC不了题。

6.2 答题时间分配的实战策略

笔试时间通常在90到120分钟之间,题量大概40到60道,要合理分配时间,我的建议是:

  • 前10到15分钟,快速做完选择题和判断题。遇到拿不准的题不要死磕,先按直觉选一个并标记,等后面有时间再回头。
  • 接着用20分钟左右做简答题。不用写长篇大论,踩点作答,分条列点写清楚。
  • 数据库和SQL题用15分钟左右,注意审题,尤其是多表关联和索引设计的细节。
  • 最后留出40到50分钟做编程题,这是拿分大头。如果编程题不止一题,先做自己最有把握的那道,保证至少AC一道,再考虑第二道。
  • 场景设计题如果留在最后,至少要把思路和核心方案写出来,不要留白。

特别是编程题,在线编程平台(牛客、赛码网)的输入输出格式有差异。有的平台要自己写Scanner解析标准输入,有的平台是函数输入输出。我的建议是去牛客上练几道io练习题,把Scanner和BufferedReader两种读法都提前跑一遍,避免正式笔试时因为IO处理卡壳。

另外,在线判题平台对时间复杂度和空间复杂度有限制,如果算法思路对了但还是超时,考虑是否需要改用更高效的数据结构,而不是死磕同一个算法。有一道题要特别注意:如果输入规模在10^5级别以上,O(n²)的暴力解法基本上会超时,这时就得考虑二分、哈希表或者双指针优化。

途虎这套试卷B还有一个特点:题目题干往往比较长,会先描述一个业务场景,再问你实现方案。读题时先抓关键词,"并发""一致性""性能""可用性"这些词出现的位置,决定了你答题的侧重点。不要被场景中的多余信息干扰,本质上考点就在几个固定领域里。

刷完这套卷,我个人最大的感受是:它不像一些大厂卷子那样追求偏题怪题,而是在反复考察基础功底的深度和业务场景的敏感度。如果你在准备途虎的秋招,或者把途虎笔试当练手,建议把本文提到的几个重点模块——集合、并发、JVM内存、MySQL索引与事务、Redis缓存、线程池——逐项夯实,每块都能说出原理且能上手写代码,笔试通过的概率会高出很多。最后分享一个我自己用过的小技巧:把每套试卷里做错的题整理成一份带错因分析的笔记,考前只看笔记里的错题和对应的知识点导图,比重新刷题效率高得多。

功能测试面试题-1 1、测试结束的标准是什么?   用例全部测试。   覆盖率达到标准。   缺陷率达到标准。 其他指标达到质量标准 2、Alpha测试与beta的区别? Alpha测试在系统开发接近完成时对应用系统的测试;测试后仍然会有少量的设计变更。这种测试一般由最终用户或其它人员完成,不能由程序或测试员完成。 Beta测试当开发和测试根本完成时所做的测试,最终的错误和问题需要在最终发行前找到。这种测试一般由最终用户或其它人员完成,不能由程序员或测试员完成。 3、比较负载测试,容量测试和强度测试的区别? 阅读详情

相关推荐

软件测试面试题:一台客户端有三百个客户与三百个客户端有三百个客户对服务器施压,有什么区别? ?

300 个用户在一个客户端上,会占用客户机更多的资源,而影响测试的结果。线程之间可能发生干扰,而产生一些异常。300 个用户在一个客户端上,需要更大的带宽。IP 地址的问题,可能需要使用 IP Spoof 来绕过服务器对于单一 IP 地址最大连接数的限制。所有用户在一个客户端上,不必考虑分布式管理的问题;而用户分布在不同的客户端上,需要考虑使用控制器来整体调配不同客户机上的用户。同时,还需要给予相应的权限配置和防火墙设置。 你工作中遇到最具价值的bug,就是重大bug咯,例如app性能测试测哪些,那你就看

一亿并发的博客 1017

软件测试功能测试+自动化测试面试题(含答案)

最近有童鞋和我抱怨,说网上很难搜到那些全面又合适的自动化测试面试题,这里根据我个人的经验以及收集整理的:你没看错,不慌,慢慢来。先从什么是自动化测试开始说起哈!什么是自动化测试?当我第一次知道自动化测试的时候,除了知道“自动化”这三个有些高大上的称呼之外,我对自动化测试一无所知,正如谈恋爱一样,找女朋友之前要知道她是谁。自动测试就是把以人为驱动的测试转化为机器执行的一种过程,它是一种以程序测试程序的过程。嗦嘎!那么...

jj2772367224的博客 3237

途虎养车测试笔试真题解析:O2O业务与自动化考点全拆解

软件测试是保障互联网产品质量的核心环节,而测试用例设计、自动化测试框架与业务场景分析则是面试笔试中反复出现的根基能力。从功能测试到接口自动化,从移动端App到汽车后市场O2O链路,测试工程师不仅要掌握等价类、边界值等基础方法,还需理解Appium、pytest等工具在真实工程中的应用价值。在垂直行业场景下,测试还延伸至线上下单、门店履约、支付回调、智能座舱等复杂业务,性能与安全测试同样不可忽视。本文以途虎养车2023秋招测试笔试试卷为例,完整拆解命题逻辑、高频考点与答题模板,帮助候选人掌握通用测试思维并适配

weixin_30684743的博客 401

功能测试面试题总结

1.1面试题:在实际测试中如果出现不可复现的bug怎么办? 经过多次复现后,还是没有出现。此时在本地记录当前的问题 回顾当时操作的流程及测试环境的配置要求,确认是否由于操作失误或者环境临时故障引起 请开发协助(自己)查找当前测试模块是否有对应的日志信息(日志的位置可以问开发) 再考虑更换一套环境查看是否能够复现上述问题 在后续的版本中测试,此时需要关注当时测试该功能时是否还出现上述的问题 在后续版本还出现过,需要开发协助打印日志进行分析定位,同时测试需要提交bug 1.2面试题:你

2301_78567583的博客 3316

途虎养车2023秋招Java笔试试卷B:考点拆解与答题策略

在Java后端校招笔试中,集合框架与并发编程往往是区分基础扎实程度的关键板块。以HashMap为例,负载因子0.75并非随意取值,而是空间与时间的折中;又如线程池的参数与任务提交流程,需要从核心线程数、等待队列到拒绝策略形成完整推导链路。理解这些底层原理,不仅有助于应对选择题中的细节陷阱,也能在编程题中写出更可靠的并发代码。实际业务场景如订单、库存、技师调度等,都对并发读写与数据结构选型提出了较高要求。本文以途虎养车2023秋招Java笔试试卷B为样本,系统拆解试卷的题型分布、高频考点、编程题思路与答题节奏

weixin_30567225的博客 334

vSphere环境证书更新/续订案例及注意事项

早期VMware的用户随后“十年之期”已到,陆陆续续出现各种登录问题,证书报错告警等,主要包含6.5到7.0的版本,VMware服务器虚拟化环境涉及的证书及类型较多,在实际更新中会出现各种奇怪的问题,有些坑耗费了大量的troubleshooting时间,极端情况下一天能看到十多遍如下报错😂本文整理了一个近期的更新案例,以及之前处理过的一些问题,仅作参考CA根证书颁发机构,用于签发其他组件的证书,其他证书的有效期不会超过根证书。

RaySun的技术Blog 3174

途虎养车2023秋招算法笔试B卷考点解析与备战策略

算法笔试是技术岗求职中检验基础与实战能力的关键环节,字符串匹配与动态规划更是其中高频考察的核心。以KMP算法的next数组推导为例,理解最长相等前后缀的本质,比机械记忆模板更能应对灵活变题;而带权区间调度、背包问题变体等动态规划模型,则要求候选人具备从业务场景抽象最优解的能力。随着产业互联网发展,机器学习基础与粒子群、模拟退火等智能优化算法也开始频繁出现在笔试题中,用于解决门店选址、资源调度等复杂工程问题。本文基于途虎养车2023秋招算法笔试试卷B的完整复盘,梳理了数据结构、字符串、动态规划、机器学习等考点

weixin_34297704的博客 420

4相5线步进电机驱动原理

步进电机驱动

while(1) 3万+

途虎养车Java秋招笔试题A卷深度复盘:考点、陷阱与备考策略

在互联网技术面试中,Java基础、并发编程与数据库优化始终是核心考察维度。理解线程池的工作原理、HashMap与ConcurrentHashMap的底层差异,以及SQL索引失效的常见场景,是构建扎实技术能力的关键。这些原理不仅用于应对笔试,更直接决定线上系统的稳定性与性能表现。从业务场景出发,例如高并发库存扣减、多门店订单统计等真实需求,能够帮助开发者将知识点转化为工程实践能力。途虎养车秋招Java笔试试卷A正是这一理念的典型体现,其题目紧密贴合汽车后市场业务,重点考察候选人对Java核心语法、JVM内存模

weixin_34323858的博客 424

途虎养车2023秋招Java笔试题B全解析:从集合源码到分布式场景

Java作为后端开发的核心语言,其笔试考察往往不仅停留在语法表面,更深入到集合源码、并发机制、JVM原理等底层实现。理解HashMap的扩容与put流程、ThreadPoolExecutor的七个参数设计、volatile的内存屏障原理,是构建扎实Java功底的关键。这些技术能力在汽车后市场电商、订单履约、库存调度等真实业务场景中具有极高价值。同时,Spring Boot自动装配、MySQL索引优化、Redis缓存穿透与分布式锁,都是支撑高并发系统稳定运行的必备技能。本文基于途虎养车2023秋招Java笔试

weixin_33812433的博客 296

Mem0 vs LangMem vs Letta:三大AI记忆框架实战对比(附代码示例)

本文深入对比了Mem0、LangMem和Letta三大AI记忆框架的核心特性与适用场景。Mem0提供开箱即用的记忆管理服务,适合快速集成;LangMem深度融入LangGraph生态,适合复杂工作流;Letta则采用虚拟内存系统,旨在突破上下文窗口限制。文章通过实战代码示例,为开发者选择适合其业务需求的AI记忆解决方案提供了清晰指南。

g9h0i1的博客 638

途虎养车2023秋招测试笔试题全解析:测试工程师核心考点与备考策略

测试工程师笔试的基础在于理解软件测试的核心概念,包括测试用例设计、接口测试与自动化测试等。这些方法论强调从功能正确性、异常边界到业务规则的全方位验证,是保障系统质量的根本原理。掌握这些技术,不仅能提升测试效率,更能构建全链路质量保障体系,具有重要的工程实践价值。在汽车后市场应用中,车载测试、智能座舱测试等场景对测试人员提出了复合能力要求,需要将通用测试技术与业务逻辑深度融合。围绕途虎养车2023秋招测试笔试试卷,拆解其题型设计与考察逻辑,梳理高频考点与答题思路,为求职者提供系统化的备考参考。

weixin_34293911的博客 474

【Linux】常见指令的使用

Linux常用指令

qq_74897518的博客 1470

途虎养车2023秋招测试笔试题B卷超全解析:从用例设计到业务场景

软件测试是保障产品质量的核心环节,而测试用例设计更是其中基石。等价类划分、边界值分析等方法,能帮助测试人员用最少用例覆盖最多有效场景,提升测试效率与准确性。在实际工程中,接口测试与数据库校验同样关键,HTTP状态码与SQL聚合查询的正确理解,往往决定了问题定位的深度。随着业务复杂度提升,具备“线上+线下”闭环思维的业务场景测试日益重要,例如途虎养车这类汽车后市场平台,其预约、优惠券、库存联动等场景,对测试工程师提出了更高要求。本文基于途虎养车2023秋招测试笔试试卷B,系统拆解核心题型与解题思路,覆盖用例设

weixin_30587927的博客 353

途虎养车2023秋招算法笔试题解析:从KMP到业务场景

算法笔试是技术招聘中筛选候选人的关键环节,其核心在于考察数据结构、算法基础与工程思维的综合运用。以字符串匹配为例,KMP算法的next数组构建逻辑、堆排序的下沉操作以及机器学习中的聚类与优化算法,都是高频考点。理解这些基础原理,不仅有助于应对笔试,更能为实际业务中的供应链调度、用户画像与推荐系统提供技术支撑。从经典算法到启发式优化方法,再到业务场景中的问题拆解,掌握算法背后的通用思维,是技术人成长的重要路径。本文结合途虎养车2023秋招算法笔试试卷A,梳理了KMP、排序、聚类、优化算法等关键考点的本质与实战

weixin_34127717的博客 458

手把手教你用Logisim搭建5级流水MIPS CPU(附华中科技计组实验避坑指南)

本文详细介绍了如何使用Logisim从零构建5级流水MIPS CPU,特别针对华中科技大学计算机组成原理实验提供实用指南。内容涵盖单周期CPU过渡、流水线寄存器设计、数据冒险处理等核心环节,并附有实验避坑建议和性能优化技巧,帮助学生高效完成这一挑战性项目。

weixin_42532938的博客 351

途虎秋招测试笔试复盘:从业务场景到自动化测试全解析

在汽车产业智能化与网联化趋势下,车载测试与智能座舱测试成为质量保障的热门方向。测试工程师不仅要掌握功能、接口、性能等基础测试理论,还需理解业务场景与自动化测试工具链的落地。从pytest接口自动化到Appium移动端自动化,从缺陷分析到CI/CD集成,系统化的测试思维是保障产品稳定性的核心。汽车后市场业务如线上下单、到店核销、保养提醒等场景,对测试用例设计提出了更高要求。本文结合途虎养车秋招测试笔试真题,梳理计算机基础、测试方法、业务场景设计、自动化工具等高频考点,帮助求职者构建从理论到实践的完整知识体系。

weixin_30386713的博客 397

基于OkHttp3+Retrofit2+RxJava3搭建响应式网络框架

本文介绍了一个基于OkHttp3+Retrofit3+RxJava3的响应式网络框架设计。该框架采用分层架构,OkHttp3负责底层网络请求,Retrofit3封装HTTP接口,RxJava3实现响应式流处理。核心亮点包括:1)高度解耦的分层设计;2)统一的异常处理和响应体封装;3)RxJava3实现线程切换和请求管理;4)MVVM架构适配,业务层只需关注数据逻辑。框架提供了完整的实现方案,包括全局配置、拦截器、响应转换、接口声明、仓库封装等组件,并支持生产环境扩展功能如Cookie持久化、Token刷新等

fuge2008的博客 847

软件测试

1、问:你在测试中发现了一个bug,但是开发经理认为这不是一个bug,你应该怎样解决? 首先,将问题提交到缺陷管理库里面进行备案。 然后,要获取判断的依据和标准: 根据需求说明书、产品说明、设计文档等,确认实际结果是否与计划有不一致的地方,提供缺陷是否确认的直接依据; 如果没有文档依据,可以根据类似软件的一般特性来说明是否存在不一致的地方,来确认是否是缺陷; 根据用户的一般使用习惯,...

weixin_30652271的博客 3413

别让假装努力毁了你,最强的68道软件测试基础问答题你能答的溜嘛?

引言: 大家好,我是 一菲,今天我们来通过问答的方式来聊聊测试基础知识有哪些?我总结了下面的68道问答题,自我感觉是比较全面的,不全的话,也欢迎小伙伴们私信我把它再补充一下,争取成为最全的武功秘籍。因为这篇文章是满满的干货,篇幅略长,大概要花15到20分钟的时间才能看完,大家准备好了,谁坚持到最后谁就是王者。 正文: 1你在测试中发现了一个 bug ,但是开发经理认为这不是一个 bug ,你应该怎样解决。 首先,将问题提交到缺陷管理库里面进行备案。 然后,要获取判断的依据和标准: 根据需求说明书、产品说明

自动化测试技术栈 2086

手把手完整实现STM32+ESP8266+MQTT+ThingsCloud云平台+Android App及iOS App及微信小程序及Web App应用

本教程文章完整记录实现STM32+ESP8266+MQTT+ThingsCloud云平台+Android App及iOS App及微信小程序及Web App应用。

嵌入式阿齐Archie 1万+

程序员面试之软件测试面试问答

1、问:你在测试中发现了一个bug,但是开发经理认为这不是一个bug,你应该怎样解决? 首先,将问题提交到缺陷管理库里面进行备案。 然后,要获取判断的依据和标准: 根据需求说明书、产品说明、设计文档等,确认实际结果是否与计划有不一致的地方,提供缺陷是否确认的直接依据; 如果没有文档依据,可以根据类似软件的一般特性来说明是否存在不一致的地方,来确认是否是缺陷; 根据...

weixin_30832405的博客 1929

Python语言基本语法元素

Python语言的基本语法元素:包括Python程序的格式框架,语法元素名称,数据类型和语句元素以及基本的输入输出函数。

Python全栈系列 8335

常见的50道经典功能测试面试题及答案

作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

2301_79535544的博客 1346

测试:面试问题(多精全)

目录面试问题 1,你们原来项目的测试流程是怎么样的? 2,你介绍下,你最熟悉的项目? 3,你们原来项目的主要的功能模块有哪些,你主要负责哪些模块? 4,你说原来充值功能,你是怎么测试的? 5,产品是怎么上线的? 6,你提交的bug,开发不认可怎么办? 7,对应无法重现bug,应该怎么处理? 8,原来项目有遇到哪些经典的bug,你是怎么发现的,最后怎么解决的? 9,linux你是怎么用的? 10,数据库原来工作当中是怎么用的? 11, 接口测试怎么做的? 12,接口自动化测试怎么测? 13,自动化测试怎么测?

weixin_43193868的博客 4万+

STM32ADC采集波形的识别(正弦波,方波,三角波)

对ADC采集到的数据进行分析,识别波形(正弦波,方波,三角波),通过串口发送识别结果,结构优化的还不太好(就是个种方法堆在一起),个人认为程序思路比较简单。注意!!!!!!这个方法已经被淘汰了,这种波形识别方法实在是不靠谱,我新上传了一个FFT的波形识别,比这个好多了,这个大家免费下载吧

软件测试功能测试全套常见面试题【功能测试-零基础】必备4-1

",软件需求,软件概要设计,软件源代码,软件详细设计,可运行程序,软件运行环境(提交BUG应注明当前环境)。APP通常是C/S架构,比如优酷客户端是C/S,网页版B/S。软件测试定义使用人工和自动手段来运行或测试某个系统的过程,其目的在于检验它是否满足规定的需求或弄清预期结果和实际结果之间的差别。++,,,,,/①黑盒测试无法看到内部代码逻辑,只能关注外部功能,比如给一个输入数据,看输出数据是否跟预期的结果相符,如果正确,则称该功能正确。①动态测试。...

分享测试知识 3400

ubuntu20.04 sshd离线安装包

ubuntu20.04 desktop桌面版默认是没有sshd服务的,如果要使用ssh连接又不方便联网,可以用这个离线安装包

上一篇: SG15用户端接入指南:PHP代码加密与商业交付的完整实践
下一篇: AI学习机选购避坑指南:从预期到验收的完整实操框架
weixin_30800807
博客等级 码龄11年 168粉丝 1404原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值