2023年秋招我投了建信金科的后端开发岗,拿到笔试通知之后临时抱佛脚了两天,结果上考场还是被题量打了个措手不及。整套笔试分成综合能力测评、专业基础知识和编程题三大部分,总共两个小时出头,中间还穿插性格测试,我印象最深的是专业题里既有典型的银行系保守风格,也有不少贴近业务的金融场景设计题,不是单纯背八股就能应付的。
这篇文章我按考试当天的完整流程来复盘,把具体考了什么、哪些容易错、编程题怎么拆解思路、以及我踩过的时间分配和心态坑都写清楚,给后面投建信金科或者类似金融科技公司后端岗的同学做一份参考。
1. 从投递到笔试:时间节奏与线上考试环境
先说投递到笔试的节奏。建信金科的秋招启动大概在8月底到9月初,网申投递之后,我大约过了一周半收到笔试邮件通知。邮件里明确写了笔试时间、考试时长和双机位监控要求,用的是智鼎在线的考试系统,电脑端需要打开摄像头监控,手机端要扫码登录放在侧后方作为第二机位。考前系统会先让你测试摄像头、麦克风、网络环境,这步不能省,我身边真有同学因为摄像头权限没开,进考场之后被系统反复提醒,白白浪费了热身时间。
整个笔试时长我记得是135分钟,含三个模块,模块之间可以自由切换,但每个模块内部的题目一旦提交就不能回头改。这个设计很关键,我一开始不知道,行测有一道题想先跳过去回头再写,结果点下一题之后发现回不去了,当场心态碎了一半。所以参加这类在线笔试,第一件事就是把“每道题提交即定版”的规则刻在脑子里。
再说环境准备。建议用有线网络连接电脑,别用公共Wi-Fi。我笔试那天家里路由器正好抽风,行测做到一半卡了十几秒,屏幕上一直转圈,那十几秒基本等于浪费了一道题的时间。手机机位也要提前充电、开飞行模式之后只连Wi-Fi,否则中途来电或者推送通知,会有作弊嫌疑,系统会记录切屏行为。还有个小细节,考试期间最好把电脑上的QQ、微信、浏览器标签页全部关掉,只留考试系统窗口。我当时忘了关一个后台标签页,系统弹了个“切屏警告”,虽然没直接被判作弊,但确实影响到了后面的做题状态。
2. 综合能力测评:银行系行测的拿分要点
第一个模块是综合能力测评,也就是行测,题量大概是35道左右,限时40分钟。题目类型覆盖言语理解、数量关系、逻辑推理、资料分析,跟我之前做过的银行笔试风格几乎一致,整体难度不算太高,但时间压力很大,平均一道题一分钟都不到。
2.1 言语理解与逻辑推理的陷阱
言语理解部分主要还是片段阅读和选词填空,文段来源基本是新闻评论和科普文章。这类题的难点不在读不懂,而在选项之间的干扰项设置得特别像,两个选项换了个主语或者程度词,一不留神就会被绕进去。我印象比较深的一道题是问“这段文字意在说明”,材料讲的是金融科技公司做数据治理的约束条件。选项里A是“数据治理面临技术瓶颈”,B是“数据治理需要权衡多方利益”,C是“金融科技的发展受到数据制约”,D是“数据治理应立足于技术革新”。我纠结了很久,最后选了B,因为“意在说明”这种题,正确答案一般不是对文段的表面复述,而是作者真正想表达的态度或观点。
逻辑推理那边则有几道真假话推理和图形推理。真假话题很简单,找矛盾关系就能秒杀,图形推理我反倒卡了一小会儿,是九宫格里黑白方块旋转加翻转的规律。这种题没有技巧就是硬编,平时刷行测图形推理题量不够的话,考场上一道至少要花两分钟。建议准备银行类笔试的同学,图形推理至少要练到一眼能看出常见规律(旋转、对称、数量、笔画)的水平,否则会严重挤压后面的做题时间。
2.2 数量关系与资料分析的速度策略
数量关系我记得有工程问题、行程问题、排列组合、浓度问题各一道。难度属于初中数学竞赛的入门水平,比如有一道是:“甲乙两队合作完成一项工程需要12天,甲单独做需要20天,问乙单独做需要多少天。”这类题列个方程就能解出来。但问题在于,35道行测题里,如果每道数量题都列方程精算,40分钟根本不够用。我的策略是:数量关系只做一眼能看出思路的,其余直接选一个带上题感、答案里大概率出现的选项——但前提是你真的知道行测题常见的选项分布规律。这种“放弃策略”听起来不太光彩,但银行系笔试的通过分数线通常不是满分导向,而是优先级导向,把时间和精力投到有把握拿分的题目上才是对的。
资料分析通常给一组表格数据,问增速、占比、同比增长之类的,计算量有点大,好在可以用计算器功能。智鼎在线系统自带一个简单的计算器,但用起来很别扭,鼠标操作比手按计算器慢得多。我的建议是提前在草稿纸上列好公式,再用系统计算器算,减少反复切换视线的次数。资料分析题一定要往后放,先把前面性价比高的判断推理和言语题拿下。
2.3 性格测评的隐藏规则
行测结束之后紧接着是一大堆性格测评题,我记得大约有80到100道,限时30分钟。题干基本都是“我喜欢独立完成工作”“我常常感到焦虑”“我乐于帮助同事”这类陈述,让你从“非常不符合”到“非常符合”里选。性格测评没有标准答案,但确实有隐藏规则:不要连续选择太极端的选项,前后题目之间要保持一致性,否则系统会判定你在“掩饰”,直接导致测评报告可信度下降。
我当时做性格测评时踩了个不大不小的坑。有一道题问“我在团队中常常主动承担领导角色”,我选了中等符合,结果过了二十几道题之后又来了一道几乎同义的表述,我鬼使神差选了个“非常不符合”,估计系统会给我在“领导力”这个维度上打个低稳定度的标签。这种题就是用来测一致性的,宁可从头到尾都选“比较符合”,也别一会“非常符合”一会“不太符合”,稳定性比真实性重要。
3. 专业基础知识:八股、场景题和冷门细节
专业基础知识部分是整套笔试里占比最高、也最需要认真对待的一块,题量大约65道,限时60分钟。题型是单选、多选和判断,覆盖面包括Java基础、数据库、计算机网络、操作系统、数据结构与算法,还有大概10道金融科技业务场景题。整体难度中等偏上,比纯互联网大厂的后端笔试要基础一些,但比一般银行的IT笔试要深一截。
3.1 Java基础:集合框架和JVM是绝对重点
Java相关的题目大概考了20道左右,高频考点集中在集合框架、JVM内存模型、并发编程、异常机制。
集合框架的部分,HashMap再一次不出意外地出现了。考的是“JDK 1.8中HashMap在什么条件下将链表转换为红黑树”。选项里有“链表长度达到8且数组长度达到64”“链表长度达到8”“链表长度达到6”“数组长度达到64”。答案是前者,且要记清楚,解除红黑树退回链表的阈值是6,中间留了个缓冲,避免在阈值附近反复转换。这种题没什么技巧,就是背,但一定要背得精确,很多写代码多年的人也会把条件记漏了“数组长度达到64”这个前提。
JVM那边考了“下列哪些区域属于线程私有”。选项是堆、虚拟机栈、方法区、程序计数器。答案是虚拟机栈和程序计数器。这个知识点不难,但容易和“运行时数据区”的整体结构混在一起。我记得当时还有个多选题问“哪些情况会触发Full GC”,选项有老年代空间不足、元空间不足、System.gc()显式调用、年轻代空间不足。正确答案是前三个,最后一个“年轻代空间不足”触发的是Minor GC而不是Full GC。这种题就是考你对GC触发时机的区分,不看源码很难答对,我因为之前看过《深入理解Java虚拟机》这本书,才勉强有把握。
并发编程考了volatile的可见性与有序性、synchronized的锁升级过程、ThreadLocal的原理。其中一道题问“volatile能否保证原子性”,这其实是基础中的基础——volatile只保证可见性和有序性,不保证原子性。但如果要考得更深,就会问“为什么volatile修饰的long和double在64位JVM上是原子性的,而int在32位JVM上可能不是”,这背后其实涉及JVM规范对64位数据类型读写的要求。好在笔试没考这么深,否则我又要当场懵。
3.2 数据库:SQL语句、索引和事务隔离级别
数据库大概考了15道题,难度属于“你觉得自己会写SQL,但一到细节就露馅”的那种。
有一道题是给一个员工表和部门表,要求查“每个部门工资最高的员工姓名”。这类问题在LeetCode上属于中等偏下的SQL题,标准解法是用窗口函数ROW_NUMBER() OVER(PARTITION BY dept_id ORDER BY salary DESC)取排名为1的记录。但笔试里可能不给窗口函数,只允许写标准SQL,这时候就得用子查询或者JOIN加GROUP BY来做。我写的是:
SELECT e.name, e.dept_id, e.salary FROM employee e JOIN ( SELECT dept_id, MAX(salary) AS max_salary FROM employee GROUP BY dept_id ) t ON e.dept_id = t.dept_id AND e.salary = t.max_salary;这个解法的逻辑是对的,但如果同一个部门有两个相同最高工资的员工,这条SQL会把两个人都查出来,符合题目要求。不过如果题目说“每个部门只返回一个人”,就得再加一层员工ID的排序条件。这种细节就是笔试里的拉分点,能把边界情况考虑进去的人,跟只会写基本JOIN的人,差距就出来了。
索引那块考了几个经典问题:最左前缀原则、覆盖索引、索引失效场景。有一道判断题说“对索引列使用函数运算会导致索引失效”,这是对的,例如WHERE YEAR(create_time) = 2023 就没法用create_time列上的索引。优化方式是改成范围查询:WHERE create_time >= '2023-01-01' AND create_time < '2024-01-01'。这一改,索引就能用上了。这种“判断对错+给出优化”的题目,在银行系笔试里很受欢迎。
事务隔离级别自然也没少考,问“InnoDB默认的隔离级别是什么”,答案是REPEATABLE READ。还问了一道有点绕的:“在REPEATABLE READ隔离级别下,事务A第一次查询了一条记录,事务B插入了一条满足相同查询条件的新记录并提交,事务A再次执行相同查询,能否看到新记录?”答案是看不到,因为InnoDB在REPEATABLE READ下用了MVCC快照读,事务启动时的ReadView在整个事务期间保持不变。但如果把读换成当前读(SELECT ... FOR UPDATE),就能看到新记录。这道题能答对的人,说明对MVCC和当前读的理解是过关的。
3.3 计算机网络与操作系统:常见但容易被考倒
网络相关的题目数量不算多,大概8道左右,但覆盖面很典型,TCP三次握手、HTTP与HTTPS的区别、Cookie与Session的区别都考了。
三次握手考的形式比较有意思,不是直接问“为什么需要三次握手”,而是给了一个场景:“客户端发送SYN后,收到服务器的SYN+ACK,此时客户端进入什么状态?”答案是SYN_SENT状态会切换为ESTABLISHED状态。这个题本身不难,但如果没背过TCP状态迁移图,很容易跟服务端的状态搞混。服务端在收到SYN后进入SYN_RCVD,收到ACK后才变成ESTABLISHED,两者的状态切换路径不一样。
HTTP和HTTPS的题目问的是“HTTPS建立连接时,SSL/TLS握手发生在TCP握手之后还是之前”。答案是TCP三次握手完成之后,才开始TLS握手。如果对HTTPS的建立流程不熟悉,这道题也容易栽。
操作系统那边我记得重点考了进程间通信方式和死锁。进程间通信的多选:管道、消息队列、共享内存、信号量,四个全选。死锁的四个必要条件:互斥、持有并等待、不可剥夺、循环等待——也是全选。这类多选题的陷阱在于,如果选项里混进一个“资源共享”,就会变成干扰项,因为资源共享其实是一种破坏死锁的方式,而不是死锁产生的必要条件。这种细节辨析恰恰是整套笔试里出题人的惯用伎俩。
3.4 金融科技场景题:建信金科的特色考点
这个板块是我觉得最有特色的部分。建信金科本身是建设银行旗下的金融科技子公司,业务方向集中在银行核心系统、支付结算、信贷风控这类系统,所以笔试里自然会考到金融业务和分布式系统的交叉知识点,大概有10道左右。
我记得比较清楚的一道题是:“在支付系统中,如何处理重复支付请求?”选项包括“在应用层生成全局唯一请求号进行幂等控制”“在数据库层对订单号建立唯一索引”“通过分布式锁控制并发更新”“所有请求都直接转发到支付通道”。正确答案是前三个。这个题很典型,它就是后端开发中幂等性设计的现实场景——用户连续点击两次支付按钮,系统收到的其实是两个一模一样的支付请求,如果没有幂等控制,用户就会被扣两次钱。解决办法就是在入口层生成唯一业务流水号,数据库层对流水号建唯一索引,双保险。
另一道题是“分布式事务的解决方案有哪些?”选项里出现了2PC两阶段提交、TCC模式、本地消息表、最终一致性。这道题考的是对分布式事务基础方案的了解。建信金科这种做银行核心系统的公司,对数据一致性极度敏感,所以这类题出现得毫不意外。作答时可以按思路写:2PC适合强一致性场景但性能较差;TCC是业务层面的补偿机制,适合银行转账这类跨系统操作;本地消息表和最终一致性则适合对实时性要求不高的场景。
还有一道题印象深刻:“分库分表之后,一张订单表被拆分到16个库的128张表,查询用户所有订单时应该怎么做?”选项里有“路由到用户ID对应的分片”“使用中间层聚合所有分片结果”“在应用层做全量扫描”“禁止此类查询”。正确答案是前两个。这道题问的不只是分库分表,还暗含了对数据路由和查询聚合的理解。
4. 编程题复盘:三道题从读题到AC的完整思路
最后进入编程题环节,三道算法题,总分占比30%左右。限时35分钟,语言自选,我用的Java。整体难度递进明显:第一题可以说是白送分,第二题需要一点数据结构和滑窗的思想,第三题则是动态规划,考场上能完整AC的人不多。我按当天实际做的顺序来复盘。
4.1 第一题:字符串中所有数字之和
题目大意是给定一个字符串,提取字符串中所有连续的数字子串并求和。比如输入“abc123def45gh6”,输出应该是123 + 45 + 6 = 174。如果两个数字子串中间被字母隔开,就算两个独立的数字;如果中间没有字母分隔,整个连续的数字串算一个数。
这道题在LeetCode里连简单题都算不上,解法就是一趟扫描,维护一个cur变量表示当前正在累积的数字。当遇到数字字符时,cur = cur * 10 + (c - '0');当遇到非数字字符时,把cur加到sum里,然后把cur重置为0。需要注意最后字符串以数字结尾的情况,循环结束后还要再累加一次cur。我写的代码如下:
public int sumOfNumbersInString(String s) { int sum = 0; int cur = 0; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (Character.isDigit(c)) { cur = cur * 10 + (c - '0'); } else { sum += cur; cur = 0; } } sum += cur; return sum; }这道题真正的坑在于:如果题目要求“忽略前导零的数字子串”,比如“007”算7还是算007,会影响你用的是字符串拼接还是数学累加。我遇到的题目没有这个额外要求,所以数学累加就可以。写完之后我特意检查了“字符串为空”和“全部是数字”这两个边界case,确认无误。这类白送分的题,不能省这一步自查,因为在线OJ的判定很严格,少一个边界判断就少一个用例通过。
4.2 第二题:和为K的最长连续子数组长度
题目大意是给定一个整数数组和一个整数K,求数组中所有和等于K的连续子数组中,长度最长的是多少。数组长度不超过10万,元素有正有负。
这道题的经典解法是前缀和加哈希表,时间复杂度O(n)。核心思路:遍历数组时维护一个从开头到当前位置的前缀和preSum。如果存在某个位置j,使得当前位置i的前缀和preSum[i]减去前缀和preSum[j]等于K,那么从j+1到i这一段子数组的和就是K。也就是说,只要在哈希表里记录过preSum[i] - K这个值出现的最早位置,我就能快速算出以当前位置结尾、和为K的最长子数组的长度。
关键点在于:哈希表里存的必须是某个前缀和出现的最早下标,而不是最后一次出现的下标,只有最早的坐标才能让子数组长度最长。我写的代码大致是这个样子:
public int maxSubArrayLen(int[] nums, int k) { Map<Integer, Integer> map = new HashMap<>(); map.put(0, -1); int preSum = 0; int maxLen = 0; for (int i = 0; i < nums.length; i++) { preSum += nums[i]; if (map.containsKey(preSum - k)) { maxLen = Math.max(maxLen, i - map.get(preSum - k)); } if (!map.containsKey(preSum)) { map.put(preSum, i); } } return maxLen; }这里有一个细节我一开始差点写错:map.put(0, -1)是必须的,它表示在数组开始之前“前缀和0已经出现过”,这样才能正确处理从数组第一个元素开始正好满足条件的子数组。另外,更新map时一定要判断if (!map.containsKey(preSum)),因为我们要保留最早出现的下标,后出现的相同前缀和不应该覆盖掉前面的记录。
我在考场上差点在这道题上翻车,原因是我顺手用了一个Map<Integer, Integer>来记录前缀和,但初始化时忘了put(0, -1),结果数组开头第一段刚好和为K的情况就没算进去。后来我刻意跑了一个简单用例验了一遍才补上这个初始化。这道题也让我认识到,笔试里出错的往往不是思路,而是边界条件。
4.3 第三题:零钱兑换类动态规划
第三题果然上动态规划了,题目大意是:给定不同面额的硬币coins和一个总金额amount,求凑成总金额所需的最少硬币个数,如果不可能凑成就返回-1。这道题是LeetCode 322的原题,唯一的变化是硬币面额数组是无序的,金额最大到了10的4次方。
动态规划的定义比较直接:dp[i]表示凑成金额i所需的最少硬币数,初始化为一个很大的数(比如amount+1),dp[0] = 0。状态转移方程是:
for (int coin : coins) { for (int i = coin; i <= amount; i++) { dp[i] = Math.min(dp[i], dp[i - coin] + 1); } }这里要注意的是内外循环的顺序。上面这种写法是“先遍历硬币,再遍历金额”,属于完全背包问题的经典写法,目的是保证每种硬币可以被无限次使用。如果反过来“先遍历金额,再遍历硬币”,对于组合类问题会得到错误结果,因为不同面额的硬币排列顺序会被当作不同方案统计。
考场上我写的是基于一维数组的版本,初始化时用了一个int INF = amount + 1,因为最多只需要amount个硬币就能凑出amount,所以amount+1可以当作“不可达”的标记:
public int coinChange(int[] coins, int amount) { int[] dp = new int[amount + 1]; Arrays.fill(dp, amount + 1); dp[0] = 0; for (int coin : coins) { for (int i = coin; i <= amount; i++) { dp[i] = Math.min(dp[i], dp[i - coin] + 1); } } return dp[amount] == amount + 1 ? -1 : dp[amount]; }写完这道题之后我做了两组自测:第一组coins = {1, 2, 5}, amount = 11,期望输出3(5+5+1);第二组coins = {2}, amount = 3,期望输出-1。两组都能通过。这之后还剩大概5分钟,我检查了前两道题的代码是否有编译错误,没有多余的时间去做更多优化。
整体来说,三道编程题覆盖了字符串处理、前缀和与哈希表、动态规划三个维度,对校招工程师的核心算法能力考察得比较全面,而且难度克制在“刷过200道LeetCode基本都能做出来”的范围内,比纯互联网大厂动辄出Hard题要友好得多。但如果你完全没有刷题习惯,35分钟连读懂第三题都很难,更别说写代码了。
5. 复盘与建议:哪些坑值得后来人避开
笔试结束之后我又花了几天时间做复盘,把整场考试的流程、出错点、以及针对后续面试的补强方向都梳理了一遍。这里我把自己踩过的坑和总结的经验分成几个方面,按优先级来讲。
5.1 时间分配:行测不要恋战
我前面提到过,笔试系统不支持返回修改已经提交的题目,这意味着你的做题顺序等于不可逆的时间投资。我当时的失误是在行测的数量关系题上各给了将近两分钟,总计大概超了6到8分钟,结果直接压缩了后面专业题的检查时间。专业题才是这套卷子里真正的得分大头,行测考的是效率,专业题考的才是深度。
如果你后面参加这类笔试,我的建议是:行测部分遇到30秒内没有思路的题,直接标记一个最有可能的选项并往下走,别回头,因为也回不了头。把节省下来的时间留给专业多选题和编程题,这两块的边际收益远高于行测。
5.2 多选题的得分策略
专业题里多选题大概有10道左右,我记得评分规则写的是“少选得一部分分,多选、错选不得分”。这个规则意味着,如果你不能100%确定某个选项,宁可不选它,而不是冒险多选。我个人的策略是:只选自己有把握的选项,拿不满没关系,保证不丢分。特别是金融科技场景题那类,出题人很喜欢在一个正确选项后面紧跟一个看似正确、实际上偷换概念的选项,这时候克制比聪明更重要。
5.3 编程题环境与语言选择
编程题的在线编辑器没有任何代码补全功能,也不支持本地编译器,只能在线提交并等待运行结果。这意味着你必须对常用API非常熟悉。我写Java代码的时候用了HashMap、Arrays.fill、Math.max这类常用类和方法,如果平时写代码全靠IDE自动补全,考场上手写记忆就会出现卡壳。建议在笔试前一周专门用纯文本编辑器刷题,强迫自己把API记熟。
5.4 针对建信金科的备考侧重
金融科技公司的笔试风格其实介于银行和互联网之间。纯银行系的笔试题侧重数据库和IT基础,互联网公司的笔试侧重算法和工程场景,而建信金科的这套题明显更看重两者的交叉地带——分布式系统在金融场景中的应用。幂等性、分布式事务、分库分表、数据一致性,这些知识点在常规后端面试题里属于加分项,但在建信金科这里几乎是必考项。所以准备这类公司时,除了常规的Java基础和算法训练,一定要抽出时间补分布式系统的基础概念,不用看太深,但2PC、TCC、消息队列做最终一致性、幂等设计的几种常见方案要能说清楚。
5.5 笔试之后的衔接
笔试结束大概一周左右,我收到了在线面试的邀请通知。第一轮面试主要还是问Java基础和项目经历,笔试里的专业题会被面试官拿来当引子,比如问我“你笔试里写的那条SQL,如果两张表的数据都到千万级别,该怎么优化”。所以笔试做完之后,不要立马把题目忘干净,趁记忆还热,把不确定的知识点查漏补缺一遍,尤其要搞清楚你当时蒙对或答错的那几道题,面试官往往会顺着你的薄弱点深挖。
最后再分享一个体会:建信金科这套笔试最大的特点不是难,而是全。它考的不只是你会不会写代码,还在考察你是否具备一个后端工程师在金融行业里生存所需的综合素养——既要有扎实的计算机基础,也要有业务上的敏感性。准备过程中我最大的教训就是前期太偏重算法刷题,忽略了行测的节奏训练和金融场景知识储备。如果你正准备投递这家公司,建议这三块都别落下,才不会像我一样,在交卷前最后五分钟还在为一道JVM多选题反复纠结。