news 2026/9/7 22:17:45

用友秋招Java笔试题复盘:从基础算法到企业级开发思维

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用友秋招Java笔试题复盘:从基础算法到企业级开发思维

我当年投用友的时候,笔试拖了两个星期才开考,心情已经从“得好好准备”变成了“求个整场体验”。结果打开答题页一看,还真没白等——这份题不是那种背了八股文就能过的卷子,它更在意你有没有真正写过代码、调过SQL、理解过一个系统从哪里开始出问题。三年后回头看,这些题目几乎就是把“企业级Java开发”这个岗位每天要面对的活儿浓缩成了两小时。用友2017秋招笔试题(二)很多人只把它当一道面试题,但如果你肯把每道题背后的意图拆开,它会告诉你一家做ERP起家的老牌软件公司,到底在筛选什么样的人。

这篇文章我就用当时的回忆加后来的复盘,把这份笔试题里印象最深的部分重新过一遍。不是对着答案念,而是从“题目为什么这么出”这个角度展开,顺便把编程题、数据库题、逻辑题里最容易丢分的地方和应对方法一并写清楚。无论你是正在准备校招的应届生,还是想去企业级软件公司做开发、想补一补基础的人,这篇都值得花二十分钟看完。

1. 为什么用友会单独安排一场技术笔试,它的考察风格又是什么

1.1 用友的Offer流程和笔试定位

用友2017年的秋招流程大致是:网申、在线笔试、技术一面、技术二面、HR面。流程看起来和大多数公司没区别,但笔试这张卷子的分量比很多互联网公司要重,原因是用友的产品线太多了,从大型ERP到云平台再到行业解决方案,每一类产品对候选人的能力和加分项要求都不一样。笔试不单单是筛人,它同时还承担着“分流”的功能——你适合做财务相关产品、还是做供应链相关的模块、还是做平台底层,笔试成绩和答题侧重会在后续面试中被参考。

我当时投的是Java开发方向,第二场笔试属于技术综合卷,题型大致分为选择题、编程题、SQL题、逻辑推理和情境题。编程题不像LeetCode那样要求你上最优解,它更偏向于考察“你能不能在有限时间内写出一段能跑的、逻辑完整的代码”;SQL题也不是裸考背语法,它需要你真的在本地跑过类似的需求。

这个风格其实和用友的业务属性直接相关。用友核心产品是给企业用的,企业软件最重要的不是炫技,而是正确、稳定、易维护。一道你写出了时间O(n)但代码读不下去的题,和一道O(n²)但结构清晰、边界完整的题,笔试官大概率会倾向后者。

1.2 技术岗笔试题的组成结构

我记得这份卷子题量不小,两个小时的在线笔试系统里,选择题大概有二十道左右,编程题两道,SQL一道,逻辑题两三道,最后还附了一道场景题。选择题里Java语法、集合类、数据库隔离级别占了一半,还有几道是计算机网络和操作系统的基础题,比如TCP三次握手、进程线程区别这些。

编程题则更贴近“工程实现”,不是那种需要数学推导才能解出来的竞赛题。只要你认真学过数据结构,做过几十道基础算法题,是完全可以做出来的。但为什么很多人考完觉得“都做出来了,怎么没过”?多半是题里的坑没有看出来,或者代码在细节上漏了边界条件。用友作为老牌企业软件公司,校招笔试的宗旨很朴素:不要求你算法超神,但要求你基础扎实、考虑周全。所以整张卷子与其说是“考试”,不如说是一块试金石,试的是你有没有形成开发者的思维习惯。

2. 编程题的考点拆解:两道题背后的“工程思维”

2.1 数组去重问题:时间、空间和排序之间的权衡

编程题第一道我记得很清楚,给定一个未排序的整型数组,要求把重复元素去掉,返回新数组的长度,并保持相对顺序。题目看起来很简单,但“保持相对顺序”这个条件很阴。如果你一上来就调用排序再相邻去重,长度确实算对了,但你改变原有数组,这在很多场景下是不允许的。

这道题的常规思路有两种。第一种是HashSet加计数器,一次遍历把出现过的元素记录下来,没出现过的放进结果数组,同时更新长度。这种做法时间复杂度O(n),额外空间O(n),写法也简单。

public int removeDuplicates(int[] nums) { if (nums == null || nums.length == 0) { return 0; } Set<Integer> seen = new HashSet<>(); int index = 0; for (int i = 0; i < nums.length; i++) { if (seen.add(nums[i])) { nums[index++] = nums[i]; } } return index; }

但很多人在笔试时忽略了一件事:题目在数据范围里很可能有“值范围很小”这个隐藏前提,比如元素取值在0到100之间。这时候用boolean数组代替HashSet会更省空间,也告诉面试官你能注意到题目中的限制。如果数值范围极大、分布稀疏,再用HashSet。笔试最忌讳的就是不管三七二十一,只会套一个固定写法。

这道题真正想考察的其实是“你是否能把输入条件、空间开销、稳定性三件事放在一起权衡”。一个稳定的、内存占用小的方案比一个跑得快但吃内存的方案更适合写入企业代码。在ERP系统里,一段去重逻辑可能要处理几十万行财务数据,HashSet导致的堆内存压力在某些老服务器上是不可接受的。

另外,这种题目在笔试环境还需要注意“是否允许原地修改”。有的题目写法是new一个数组返回,有的则是要求原地处理。如果你没看清题目要求,用错方式,可能函数签名都不一样。这类小坑看起来不起眼,却是笔试筛人的重要维度。

2.2 反转链表中间片段:最经典也最容易翻车的链表题

第二道编程题是反转单链表中的某一段,给定链表头节点、起始位置m和结束位置n,要求把第m到第n个节点之间的部分反转。

这道题在国内企业校招中出现频率蛮高的,因为它是“基础但容易错”的典型代表。很多人觉得链表反转太简单了,刷过好几次,但题目一旦加上区间限制,就需要引入前驱节点和后继节点,出错率就指数上升。

写这道题时最常见的错误有三个:第一个是没保存前驱节点,导致反转后链表断裂;第二个是m等于1时没有处理好头节点变动;第三个是只反转了节点值、没有真正改指针——虽然某些判题系统也能过,但面试被追问必然露馅。

我当时写的解法是经典的三个指针法,先定位到第m个节点的前驱,然后从第m个节点开始,循环n-m次,把后面的节点一个个插到前驱节点之后,这样的好处是即便m等于1也没问题。

public ListNode reverseBetween(ListNode head, int m, int n) { ListNode dummy = new ListNode(0); dummy.next = head; ListNode pre = dummy; for (int i = 1; i < m; i++) { pre = pre.next; } ListNode cur = pre.next; for (int i = 0; i < n - m; i++) { ListNode next = cur.next; cur.next = next.next; next.next = pre.next; pre.next = next; } return dummy.next; }

这道题笔试过不过的关键不在算法难度,而在dummy节点的使用,以及循环次数是否准确。如果n-m的边界不谨慎,很容易多反转一个或少反转一个。

考完这题我回想了很久,意识到它考察的是“你有多大把握写出零边界错误的代码”,这比“会不会链表反转”要高一层次。在企业开发里,类似的链表结构虽然不常见,但“某段逻辑在特定区间内生效”的需求到处都是,比如批量处理一段数据时,对区间端点做特殊判断,这种思维的严谨性是可以迁移的。

2.3 SQL题:部门最高工资,怎么答出“企业级”的感觉

SQL题的题目也很经典:查询每个部门工资最高的员工信息。表结构大概是部门表和员工表,员工表有部门编号和工资字段。很多人上来就写GROUP BY,但这条SQL里有隐藏的重名员工问题——如果同一个部门有两个员工工资并列最高,只返回一个还是都返回,题目里如果没有明确说,就要考虑用窗口函数或者子查询的方式查所有并列最高的人。

我当时的写法是用窗口函数,这是2017年PostgreSQL 9.5以后和MySQL 8.0版本已经有的功能,但很多考生还在用MySQL 5.6,窗口函数不熟悉也正常。标准答案是:

SELECT d.dept_name, e.emp_name, e.salary FROM ( SELECT dept_id, emp_name, salary, RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rk FROM employee ) e JOIN department d ON e.dept_id = d.dept_id WHERE e.rk = 1;

如果你用GROUP BY加MAX(salary),面临的问题就是员工名字没法直接关联,因为SELECT非聚合字段在严格SQL模式下会报错。这时候只能变成子查询:先查出每个部门的最高工资,再重新关联员工表。写法上可行,但在“多个并列最高”的场景下会漏数据。

这道题考察的不只是SQL语法,还有数据分析的严谨性。用友的东西面向企业客户,客户动不动就问“为什么这个报表少了一行”,你的SQL如果天生就会漏数据,在他们眼里就是不可接受。所以这道题我建议每个准备企业级公司笔试的人都认真练一练,尤其是窗口函数和CASE WHEN的配合,而不是停留在WHERE和JOIN层面。

3. 选择题里的“温柔陷阱”:Java基础与数据库隔离级别

3.1 final、finally、finalize三兄弟,为什么总考永远记不牢

选择题部分,用友出了好几道经典Java题,最典型的是final、finally、finalize的区别。这道题校招几乎是百分之百会出现的,但出题方式可以非常有迷惑性。比如题目问“下面哪个选项对final的描述是错误的”,选项里可能会出现“final修饰的方法可以被重载但不能被重写”“final修饰的引用类型变量指向的对象内容可以改变”“final static常量一定在编译期确定”这种表述。

第三个就是陷阱。final static变量不一定是编译期常量,如果它被初始化为一个方法调用的结果,比如final static int X = getX(),它就是运行期才能确定的。很多人一看到final static就默认是常量,直接选错。这个问题在企业项目里会关系到类的初始化顺序、常量的替换机制,不只是为了考试而考试。

finally和finalize的考点就相对固定。finally保证代码块一定执行,但有一个例外是System.exit(),这是个超高频考点。finalize早已不推荐使用,从Java 9开始标记为Deprecated,如果题目选项里出现“finalize由垃圾回收器调用,能保证对象被回收前执行”,这半句话是对的,但如果你回答“finalize一定会执行”,那就是错的,因为对象可能永远不会被垃圾回收,或者finalize里又抛了异常。

这类题为什么总丢分?不是不会,而是你理解得不够细。做题的时候不要只是“感觉某个选项不对”,要能在脑子里推演一遍具体的执行场景。笔试不要求你背八股到一字不差,但要求你“知道底层是怎么跑起来的”。

3.2 数据库隔离级别:答得出名字,更要答得出场景

数据库隔离级别的选择题,用友考得很有企业特色。它不会直接问你“四种隔离级别分别是什么”,而是给你一个转账场景:两个事务同时并发执行,在什么隔离级别下会出现脏读,什么级别下会出现不可重复读,什么级别下会出现幻读。

很多人记顺序用“读未提交<读已提交<可重复读<串行化”,但真到了场景应用题,还是会绕晕。其实可以把四种问题打包成一张小表来记忆:

隔离级别脏读不可重复读幻读
读未提交可能可能可能
读已提交不可能可能可能
可重复读不可能不可能可能
串行化不可能不可能不可能

MySQL默认是可重复读,这一点和Oracle默认的读已提交不一样。用友的很多产品部署在Oracle或MySQL上,如果你答不出这个区别,HR会怀疑你是不是只会背课本。

这里我要多提醒一句:笔试题目如果考隔离级别,往往还会顺带问一句“当前读和快照读的区别”。MySQL的InnoDB在可重复读级别下,普通的SELECT是快照读,但SELECT ... FOR UPDATE是当前读,所以即便在可重复读下,用当前读依然可能在并发写时出现幻读。这种细节虽然不一定在笔试里考,但如果你能主动在答案里带出这一点,后续面试会给你加很多印象分。

3.3 线程池核心参数:答出来不算完,还要能说出线程池是怎么嫌弃多余任务的

线程池的参数里,最大坑在于拒绝策略。一张经典的选择题会这么出:“当线程池的任务队列已经满了,同时线程数已经达到maximumPoolSize,此时再有新任务提交,会触发什么?”选项里是AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy。

每种策略背后的“性格”不一样:AbortPolicy是直接抛RejectedExecutionException,这也意味着任务没执行;DiscardPolicy是静默丢弃,坑在无声无息;DiscardOldestPolicy是把队列里最老的任务丢掉,然后把新任务塞进去;CallerRunsPolicy则是最温和的,它让提交任务的线程自己去执行这个任务,而不是交给线程池。

很多人在笔试时能选对“默认是AbortPolicy”,但如果你真想打动面试官,你要能解释每种策略在什么业务场景下用。比如财务系统里任务不能丢,那一般不能用Discard;如果不太在意部分统计类任务的准确性,DiscardOldestPolicy可以防止大量过期任务堆积;如果希望线程池尽量不拒绝,CallerRunsPolicy配合有界队列可以起到背压效果。

用友笔试题有一个特点:它喜欢考“你日常开发中真的会用到的东西”,而不是纯粹靠记忆的东西。线程池这套知识,你在任何一家企业级公司做Java开发都会碰到,所以它值得你花时间深入研究,而不是只背四个名字。

4. 逻辑题与情境题:企业软件公司考查的不只是编码

4.1 灯泡开关问题:不是脑筋急转弯,是“状态穷举”能力

逻辑题里有一类非常经典的:有100盏灯、100个人,第1个人把所有灯打开,第2个人把编号为2的倍数的灯状态反转,第3个人把编号为3的倍数的灯状态反转,如此类推。问最后哪些灯是亮的。

这道题小学奥数就有,但放到校招笔试题里,它的考查点是“你能不能把一个过程性问题转化成数学规律”。从程序员的视角看,它本质上是判断1到100每个数的因子个数是奇数还是偶数。因子成对出现,只有完全平方数的因子个数是奇数,所以最后亮着的灯就是编号为1、4、9、16、25、36、49、64、81、100这10盏。

如果笔试系统支持语言答题,你可以写一个模拟的代码来暴力求解:

public List<Integer> judgeLamp(int n) { boolean[] on = new boolean[n + 1]; for (int i = 1; i <= n; i++) { for (int j = i; j <= n; j += i) { on[j] = !on[j]; } } List<Integer> result = new ArrayList<>(); for (int i = 1; i <= n; i++) { if (on[i]) { result.add(i); } } return result; }

但请注意,这类题在笔试里真正扣分的点不是“解不出”,而是“解出来了,但说不出规律”。很多人直接写模拟代码,题目就过了,但后续面试如果被追问“为什么最后是这些灯亮着”,表达不清楚就直接掉档。所以做逻辑题时,不能只满足于暴力求解,一定要再往前走一步,找到数学本质。这个习惯放到真实项目里,就是“你修了一个Bug,得知道它为什么会发生,而不是改完就完事”。

4.2 情境题:客户提了一个不可能的需求,你怎么处理

用友的笔试题里还有一道情境题,大意是:你负责的模块已经开发完成,但客户突然提出一个新需求,要求在下一次发版前必须加上,而按照当前排期,这个需求根本没有足够的开发和测试时间。请问你怎么办?

这道题没有标准答案,它考查的是项目意识和沟通意识。正常的答法是:先评估需求复杂度和对现有系统的影响,再和产品经理、项目经理确认这个需求是否真的必须本次上线、是否能拆分成更小的子需求、是否能先上线核心部分其余部分后续迭代。而不是一口答应“行,我加班搞定”,也不是一口回绝“做不到”。

但想拿高分,最好再加上一句:主动和客户确认“这个需求的业务背景是什么,要解决什么问题”,很多时候客户提的原始方案只是他想象出来的实现方式,真正想要的业务效果可能用另一种改动更小的方式就能达成。这种能力,在企业软件公司的日常工作中非常关键,因为用友面对的客户经常分不清“需求”和“实现方案”的区别。如果你能从业务目标倒推,就能大大减少无意义的开发量。

这道情境题其实比编程题更重要,它决定你在一面时是否有故事可讲。笔试官看了你在这道题上的回答,基本就能推断出你有没有实习过,有没有真正在项目管理流程中待过。

5. 复盘:过了笔试的人,提前做对了哪些事

5.1 时间分配:先做SQL和情境题,最后死磕编程题

笔试的时间是有限的,但有些人在编程题上卡了四十分钟没做出来,后面SQL题和逻辑题基本靠蒙。这个策略非常不划算。用友笔试的题目设置里,选择题和SQL题的分值密度更高,逻辑题也相对容易拿分,而编程题即使只通过部分用例也有分。如果你要拿总分最大化,正确的时间分配大致是:先花10分钟快速扫完所有题目,把有把握的选择题和SQL题先做掉,再做编程题里思路最清晰的那一道,最后用剩余时间攻坚另一道。

为什么这个顺序更合理?因为在线笔试系统有一个隐藏的心理包袱:如果编程题一直通不过,你很容易陷入恐慌状态,连简单题目也开始怀疑自己。反过来,先把能拿的分稳稳拿下,心态会稳很多,做编程题的效率反而更高。

我当时自己有一个习惯:正式开考前,先在草稿纸上把编程题的两道题目要求重新抄一遍,画出输入输出示例,标出边界条件。这个习惯让我不那么容易遗漏“m和n的取值范围”“数组为空”这类细节。这招在LeetCode刷题时觉得多余,但到了笔试那种紧张氛围里,确实能救你。

5.2 代码规范:能过题不等于能拿高分

还有一点容易被忽略:笔试系统虽然只判正确性,但只要题目是人工二次审阅的,代码风格就会影响评分。用友这种公司很看重代码可读性,因为他们的代码库要经过多年、多人维护。你写的变量名如果是一堆a、b、c、d,哪怕逻辑对,面试官也不会觉得你能力强。

我当时提交反转链表那道题的时候,特意把几个辅助节点的命名写成pre、cur、next,而不是p、q、t。每一道题都加上注释,说明“这个循环在做什么、为什么这么写”。这不只是给面试官看,也是给自己复核逻辑提供帮助,注释一写,很多边界问题自己就暴露了。

经常有人问“笔试代码要不要写注释”。我的建议是:如果每行代码都写注释,太啰嗦,没必要;但在关键思想和容易看错的地方,一定要写。不是展示你有耐心,而是证明你在开发时有意识地在为后续维护者铺路。这种意识对服务企业客户的产品来说,比那多写出一个最优解更重要。

5.3 从笔试内容反推公司技术栈和业务方向

用友2017年这份笔试题透露出的技术栈信息量很大。Java、SQL、数据库隔离级别、线程池,基本圈定了一个企业级Java开发工程师的核心知识域。如果你事先去了解一下用友的产品,会发现他们的招聘方向大致分为NC产品线的ERP开发、U8产品线、以及后来的YonBIP云平台,这几条线都依赖Java技术栈和复杂SQL能力。

所以准备笔试的时候,不要只刷LeetCode,还要去搜一下这家公司的产品文档、技术博客、招聘JD。如果JD里提到“熟悉多线程、熟悉JVM内存模型、熟悉Oracle或MySQL”,那笔试题目大概率就会围绕这些东西出。反之,如果你投的是前端岗,那笔试内容肯定又是另一套,我认识的一个同学投了用友前端岗,考题是JavaScript事件循环、DOM操作和CSS布局,基本没有算法。

这个道理也许很多面试经验帖都提过,但真正会去执行的人很少。大多数人准备校招笔试的方式还是靠“猛刷题库”,而不是“按公司类型定制复习方案”。但用友、金蝶、SAP这类企业级软件公司,它们的笔试套路和互联网公司明显不一样。你拿刷字节、美团题的习惯去应对,很容易掉进“觉得题目不难,但就是过不了”的怪圈。

6. 写在最后的一次亲测提醒

如果你现在正准备这类企业级软件公司的笔试,我特别想提醒你一句话:不要小看任何一道“简单题”。用友这份卷子里的数组去重、链表反转、SQL最高工资,单拎出来每一道都很基础,但组合在一起,它就是一套完整的“开发者素质体检”——基础是否扎实、边界是否敏感、表达是否清晰、是否懂一点业务。

面试和笔试最大的区别在于,面试是人与人之间的判断,笔试是人与系统之间的判断。而系统不会给你解释的机会,你写的每一个字、每一行代码就是全部。如果你能养成“做每一步都问自己为什么”的习惯,这类笔试其实不太需要担心。我当时把考后在草稿纸上复盘题目的习惯保留了下来,后来进入工作,每次写完一段代码提交之前,我也都会先在心里跑一遍边界情况,这个习惯的源头,可以说就是这份笔试题教会我的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 22:17:44

AI落地缺的不是技术,而是有领导力的管理层

今天的 AI 已经够用&#xff0c;缺的是有领导力的管理层 这两年&#xff0c;我越来越多地听到一种抱怨&#xff1a;公司引进了大模型&#xff0c;买了 API&#xff0c;甚至搭建了私有化平台&#xff0c;结果半年过去&#xff0c;AI 项目还停留在“几个技术同学自己玩一玩”的状…

作者头像 李华
网站建设 2026/9/4 13:03:06

多 Agent 协作实战:Python 实现最小版 Bot Mode 编排与上下文隔离

很多做 AI Agent 的同学都会碰到同一种尴尬&#xff1a;单个 Agent 写周报、查资料、改代码都能完成&#xff0c;可任务一旦带上“然后”“同时”“分别”这类词&#xff0c;比如“先查最近 1 小时的线上错误日志&#xff0c;再做根因分析&#xff0c;按模块生成日报&#xff0…

作者头像 李华
网站建设 2026/9/5 11:08:26

从《后西游记》定档看AIGC长剧的工程化生产之道

看到《后西游记》定档 8 月 31 日、还要登陆湖南卫视黄金档这条消息时&#xff0c;我的第一反应不是“AI 也太强了”&#xff0c;而是“生产方式终于被摆到了台面上”。 国内首部 AIGC 长剧&#xff0c;这个标签真正值得讨论的不是某一帧画面有多惊艳&#xff0c;也不是哪个大…

作者头像 李华
网站建设 2026/9/5 19:57:25

爱奇艺2019秋招C方向笔试题解析:C语言基础与底层原理

笔试真题这种东西&#xff0c;往往比市面上绝大多数教程都值得反复咀嚼。尤其是大厂的校招C方向笔试题&#xff0c;题目看着基础&#xff0c;实际每个选项都在挖坑&#xff0c;考的是你写代码的"肌肉记忆"和底层理解。爱奇艺2019秋招C方向笔试题&#xff08;A&#x…

作者头像 李华
网站建设 2026/9/4 12:59:54

寒武纪软件岗笔试复盘:从操作系统到AI芯片软件栈的底层考察

秋招季又到了&#xff0c;不少人私信问我寒武纪软件岗的笔试到底考什么。说实话&#xff0c;寒武纪2019年秋招这套题&#xff0c;在当年AI芯片公司里算是相当有代表性的&#xff1a;既考C/C基本功&#xff0c;又考操作系统和体系结构&#xff0c;最后还要看你对AI芯片软件栈有没…

作者头像 李华
网站建设 2026/9/3 18:35:14

MATLAB曲线拟合工具箱cftool完整指南:从概念到工程实践

如果你最近在做实验数据处理、传感器标定或者信号分析&#xff0c;很可能已经遇到这样一个场景&#xff1a;手里有一堆散点数据&#xff0c;明知道它们之间存在某种规律&#xff0c;但要么手动代入公式反复试错&#xff0c;要么在 Excel 里折腾半天只得到一个粗糙的趋势线。MAT…

作者头像 李华