
1. 用友秋招笔试题的整体设计与思路拆解1.1 用友这类企业软件厂商到底在考什么先聊聊我对用友笔试的理解。用友是国内做企业管理软件的老牌厂商ERP、财务软件、人力系统这些业务占大头这些年也在往云服务转型。这类公司的校招笔试题和互联网大厂的路子其实不太一样。互联网大厂喜欢考偏算法、偏底层的东西比如手写红黑树、LRU缓存、海量数据TopK一套题做下来像参加算法竞赛。用友这种企业管理软件厂商更看重的是Java基础扎实不扎实、数据库能不能写好SQL、面向对象设计有没有感觉因为这些才是日常写业务代码真正天天用的东西。“用友2017秋招笔试题五”这个标题字面上看是整套笔试题里的第五部分。一般这类公司的笔试都是分模块的前面可能考了选择题、填空题、简答题到了第五部分大概率是综合应用题或者算法编程题这是整套卷子里区分度最高的环节。前几部分可能大家都在及格线附近徘徊一到这部分基础扎不扎实、有没有真正写过代码差距一下就拉开了。那这套题适合谁来看如果你是正在准备校招的应届生尤其是目标锁定在传统软件厂商、企业服务类公司的这份东西对你很有参考价值。如果你已经工作了一两年想跳槽去这类公司看看这些题也能帮你摸清他们的技术偏好。说白了用友这种公司的笔试题就是一线业务开发日常工作的缩影它考的不是你有多聪明而是你手上有没有真功夫。1.2 为什么“第五部分”往往是分水岭我翻过不少企业软件厂商的笔试题发现一个规律整套卷子一般按“基础题→进阶题→综合题”的梯度来设计。基础题考概念记忆比如Java的HashMap和Hashtable区别、String和StringBuilder区别这类题只要复习过八股文基本都能答个大概。进阶题开始考理解比如给一段代码让你判断输出结果、给一个SQL让你优化。到了第五部分这种靠后的模块考的就是综合运用能力了。综合题怎么考常见的有几种出法。一种是给一个业务场景让你做面向对象设计画出类图写出核心类的代码。另一种是给一道算法题让你现场手写解决方案考察代码功底。还有一种是问答题让你说说某个技术方案的设计思路重点看你的思路清不清晰、有没有工程实践经验。这里有个很关键的认知用友这类公司出算法题很少出特别偏门的竞赛题。他们更倾向于出那种“工作中会遇到的算法问题”的简化版比如字符串处理、数组操作、链表反转、递归遍历考的是逻辑思维和代码习惯而不是你要背多少种高级数据结构。所以如果你复习的方向错了天天死磕那些竞赛级难题反而容易栽在这些看似简单的题目上。2. 核心知识点拆解与答题要点2.1 Java基础集合、多线程与内存模型Java在企业管理软件领域是绝对的主力语言用友的笔试里Java占比一向很重。第五部分的综合题经常会把Java基础知识和业务场景结合在一起考。举个例子很多题目会要求你写一个线程安全的计数器或者设计一个支持并发的缓存工具类。这种题表面看是在考多线程实际上是在考你对synchronized、volatile、Lock这些并发原语的理解深度。我给大家一个复习思路。集合方面重点掌握HashMap的底层原理包括数组加链表加红黑树的结构、扩容机制、为什么线程不安全以及ConcurrentHashMap的分段锁或者CAS加synchronized的优化思路。这些不只是笔试要考面试被问的概率也极高。多线程方面要搞清楚synchronized和ReentrantLock的区别、volatile的可见性和禁止重排序、线程池的核心参数和拒绝策略。JVM方面重点看内存区域划分、垃圾回收算法、类加载机制。答题的时候有个技巧不要只写结论要把关键过程写出来。比如问HashMap为什么线程不安全你要答出多线程同时put可能导致数据覆盖、JDK7及以前还会在扩容时形成循环链表这两个点都说到才算是完整答案。这部分的题往往不难难的是你答得够不够全、够不够深入。2.2 数据库SQL编写、事务隔离与索引优化企业软件和数据库是深度绑定的用友的ERP产品底层全是大量的表结构和复杂查询所以数据库笔试题是绕不开的。第五部分如果出SQL题大概率是给一个业务表结构让你写查询语句。常见的考点有多表连接查询、聚合函数与GROUP BY、子查询、窗口函数如果允许的话、以及写一个满足特定条件的复杂查询。我给你们一个建议复习SQL一定要动手练光看不写等于白学。笔试的时候SQL题是最容易拿分也最容易丢分的容易拿分是因为答案相对标准容易丢分是因为很多人写出来的SQL有语法错误或者逻辑不对。写完之后一定要在脑子里过一遍执行过程检查表名、字段名、别名是否对得上。事务这块重点理解四种隔离级别读未提交、读已提交、可重复读、串行化以及各自解决的并发问题。读已提交解决脏读可重复读解决不可重复读串行化解决幻读。MySQL默认是可重复读级别但InnoDB通过间隙锁等手段在一定程度上避免了幻读。索引方面要知道B树索引的结构、聚簇索引与非聚簇索引的区别、最左前缀原则、回表的概念以及什么情况下索引会失效比如对索引列使用函数、隐式类型转换、左模糊匹配等。2.3 算法与逻辑难度不大但坑不少用友的算法题难度通常比互联网大厂低一个档次但坑不少。常见出题方向包括字符串处理反转、去重、括号匹配、数组操作排序、查找、移动元素、链表操作反转、删除节点、递归与分治、简单的动态规划比如斐波那契数列、爬楼梯。这些题只要刷过LeetCode简单到中等难度的题目基本都能应对。第五部分的算法题有一个特点值得注意很多题目表面上考算法实际上考的是代码规范。比如要求你写一个函数输入一个字符串输出它反转后的结果。这道题最简单的方式是用StringBuilder的reverse方法但你写出来之后考官可能更想看到你理解反转的底层逻辑。所以答题的时候如果题目没有限制API的使用用现成方法没问题但最好在注释里说明思路甚至可以给出两种写法一个是调API的一个是手写循环的这样更能体现你的水平。逻辑推理题也是这类笔试的常客。比如经典的“有8个球其中1个偏重有一个天平最少称几次能找出来”答案是两次。或者“有三个人过桥只有一个手电筒每次最多两人过桥各自过桥时间不同问最短需要多长时间”。这种题考的是思维灵活性平时多看一些经典逻辑题考试的时候就不会慌。3. 真题演练与解析直接可抄的解题示范3.1 综合题一手写线程安全的单例模式题目经常是这样的“请用Java实现一个线程安全的单例模式并说明你的实现方式的优缺点。”这道题考的是多线程基础和设计模式理解。我推荐用双重检查锁Double-Checked Locking实现并配合volatile。代码是这样的public class Singleton { // volatile防止指令重排序 private static volatile Singleton instance; private Singleton() { // 私有构造防止外部实例化 } public static Singleton getInstance() { if (instance null) { // 第一次检查避免不必要的加锁 synchronized (Singleton.class) { if (instance null) { // 第二次检查保证只有一个实例 instance new Singleton(); } } } return instance; } }为什么这里必须要用volatile核心原因在于new Singleton()这一步不是原子操作。在JVM层面它实际上分成了三步分配内存空间、初始化对象、将引用指向内存地址。如果不用volatile在多线程环境下某个线程可能拿到一个尚未完成初始化对象的引用这时候访问对象内部字段就可能出问题。用volatile修饰后JVM会禁止对这个对象引用赋值操作的重排序保证对象完全初始化之后才对外可见。这道题的回答思路我建议按这个顺序来先给出代码然后说这是双重检查锁实现为什么用volatile再说这种方式的优点是延迟加载且性能较好缺点是实现相对复杂。如果你能顺带提一句“也可以用静态内部类方式实现单例代码更简洁”那这题的分数就稳了。静态内部类的写法可以简单说两句利用JVM的类加载机制保证线程安全同时具备懒加载特性。3.2 综合题二SQL查询编写实战下面这道SQL题非常典型我几乎每年都能在面试者的回忆版笔试题里看到类似版本有三张表学生表student(sid, sname)、课程表course(cid, cname)、成绩表score(sid, cid, grade)。请查询“选修了所有课程的学生姓名”。这题有两种主流解法我建议两种都掌握。第一种是用GROUP BY加HAVING和COUNTSELECT s.sname FROM student s JOIN score sc ON s.sid sc.sid GROUP BY s.sid, s.sname HAVING COUNT(DISTINCT sc.cid) (SELECT COUNT(*) FROM course);这个思路是先按学生分组统计每个学生选修的课程数如果等于课程总数就说明他选修了所有课程。用COUNT(DISTINCT sc.cid)是为了防止同一学生同一课程有多条成绩记录的情况。第二种是用NOT EXISTS双重否定SELECT s.sname FROM student s WHERE NOT EXISTS ( SELECT c.cid FROM course c WHERE NOT EXISTS ( SELECT 1 FROM score sc WHERE sc.sid s.sid AND sc.cid c.cid ) );这个思路是找那些“不存在一门课程他没选修”的学生。从语义上看第二种写法更贴近问题描述但理解起来更抽象一些。笔试的时候我建议写第一种因为出错概率低只要逻辑对基本能拿满分。写SQL题的时候有几个细节要特别注意表名和字段名的拼写必须和题目给的一致大小写如果题目有要求就要照做没有要求的话保持统一即可。多表连接时如果字段名有歧义一定要用表别名限定否则可能报错。写完查询后在脑子里跑一遍表里的数据看看结果是否符合预期这个习惯能帮你少丢很多分。3.3 综合题三算法编程之字符串处理再来看一道常见的编程题“给定一个字符串去掉其中重复的字符使每个字符只出现一次并保持字符原有的相对顺序。例如输入‘abacdbc’输出‘abcd’。”我给出两种实现方案。第一种是暴力法用双重循环每遍历到一个字符时检查它是否已经出现在前面的字符中public String removeDuplicate(String str) { if (str null) { return null; } StringBuilder sb new StringBuilder(); for (int i 0; i str.length(); i) { char c str.charAt(i); boolean exists false; for (int j 0; j sb.length(); j) { if (sb.charAt(j) c) { exists true; break; } } if (!exists) { sb.append(c); } } return sb.toString(); }第二种是用HashSet辅助判断时间复杂度降到O(n)public String removeDuplicate(String str) { if (str null) { return null; } StringBuilder sb new StringBuilder(); SetCharacter seen new HashSet(); for (int i 0; i str.length(); i) { char c str.charAt(i); if (seen.add(c)) { sb.append(c); } } return sb.toString(); }笔试的时候如果时间和空间没有明确要求我建议直接用第二种简洁高效。HashSet的add方法返回boolean值如果字符已经存在add返回false说明重复了就跳过只有首次出现才追加到StringBuilder里。这里改用StringBuilder拼字符串因为高频字符串拼接时直接用String会不断创建新对象浪费内存。这一行细节写出来面试官一眼就能看出你有没有实战经验。这种题还有可能让你分析时间复杂度第一种是O(n^2)因为最坏情况下每一个字符都要和之前的所有字符比较一次第二种是O(n)因为HashSet的查找复杂度平均是O(1)。准备笔试的时候养成随手分析时间复杂度的习惯对面试环节也有帮助。3.4 综合题四经典逻辑推理题逻辑推理题在这类笔试里也经常出现尤其在整套题靠后的部分。我挑一道比较常见的“有9个外观完全相同的球其中1个质量与其他8个不同不知道是轻还是重用一个没有砝码的天平最少称几次一定能找出异常球并判断它是轻是重”答案是三次很多人第一次做会答两次其实是陷阱。为什么两次不够因为不知道异常球是轻还是重导致信息的确定度不够。具体称法是这样的我把9个球分成三组每组3个记为A组、B组、C组。第一次称A组对B组第一次如果平衡说明异常球在C组。第二次从A组拿两个正常球对比C组中任意两个球。如果平衡那异常球就是C组剩下的那个第三次拿它跟正常球比一下就知道轻重。如果不平衡那第二次称的两个球中有一个是异常球第三次任选其中一个跟正常球比就能判断出哪个异常以及轻重。第一次如果不平衡假设A组比B组重说明异常球可能在A组偏重或B组偏轻C组都是正常球。第二次取A1、A2、B1放在天平左边A3、C1、C2放在右边。分析一下如果左边重说明问题在A1、A2或B1之间再称A1和A2就能确定——它俩若平衡B1就是偏轻的异常球若不平衡偏重的那一个就是异常球。如果右边重说明异常球要么是A3偏重要么是B1偏轻第三次拿A3和正常球比较即可判断。如果第二次平衡说明异常球在B2、B3之间且偏轻第三次称B2和B3轻的那个就是。这种题做完之后建议把你推导过程的逻辑整理清楚别只写个答案。考官看重的往往不是那个数字而是你拆解问题的思路。4. 常见问题与笔试排查技巧实录4.1 时间分配综合题最忌讳在一道题上死磕根据我自己参加校招和后来参与校招出题的经验这类笔试的题量一般不小第五部分的综合题题量通常在3到5道之间但每道题分值占比很高。很多人最容易犯的错误是前面遇到一道不太会的题死磕了半小时结果后面简单的题没时间做。我的建议是拿分优先先把会做的题全部做完再回头啃难题。看到一道题快速判断它属于你熟悉的那一类如果是马上动手如果不是先跳过。实际考试中往往存在“后面一道题比前面那道更简单”的情况。别因为一道题影响了整张卷子的节奏。这里也顺便提醒一句实际笔试的环境往往不像平时刷题那么舒服系统可能不支持本地IDE调试也可能有自动保存的延迟所以平时练习时就要习惯直接在网页编辑器里写代码写完自己肉眼检查一遍。4.2 代码习惯这些细节分不要丢笔试阅卷的时候代码规范程度直接影响印象分。我见过太多技术栈不错、但写出来的代码乱七八糟的考生缩进不统一、变量名随意、没有注释甚至函数括号都不配套。笔试系统虽然会编译运行但如果人工阅卷的话一份整洁的代码和一份凌乱的代码哪怕功能一样得分也可能差不少。几个实用的规范建议类名大驼峰、方法名和变量名小驼峰常量全大写下划线分隔关键逻辑写注释但别写废话不要写超长函数一个函数只干一件事代码里不要出现硬编码的魔法数如果用了要说明它的含义。这些习惯你在平时刷题时就要养成别指望到考场上突然变严谨。另外特别重要的一点写Java代码记得处理空指针和边界情况。比如写一个数组相关的方法要判断数组是否为null、是否为空数组写字符串处理方法要判断字符串是否为null。这些边界情况的处理是区分“会写代码”和“写出好代码”的重要指标。4.3 环境问题别让技术之外的意外毁掉考试线上笔试最怕遇到技术问题比如断网、浏览器崩溃、代码没法保存。这里分享几个避坑建议。第一提前半小时进入考试系统测试网络、测试代码编辑器、确认登录状态。第二写一个简单的测试程序比如输出“hello world”确认运行环境正常别等正式做题了才发现环境有毛病。第三如果平台支持自动保存确认是否开启了如果不支持写完一个题就手动保存一下。第四随时留意剩余时间别等提醒弹出来了才发现快交卷了。线下笔试的话多带两支笔提前确认考场位置。这些听起来很琐碎但我确实见过有人因为签到迟到被取消考试资格也见过有人因为带了不带电池的计算机进考场闹出乌龙。笔试不只是考知识也考你能不能在场安静有序地发挥出来。4.4 经典失分点对照自查以下这些失分点是我看整套笔试题时总结的高频坑审题不清题目要求输出学生姓名你连课程信息一起输出了要求返回字符串你直接打印出来了。SQL语句执行顺序出问题分不清WHERE和HAVING的先后顺序或者聚合条件写错位置。Java集合类的空指针遍历Map时没有判空操作List时索引越界。单例模式下忘了私有化构造方法这不是单例这是每个人都能new一个对象。算法题没有考虑边界条件输入空字符串、长度为1的数组、全是重复字符的情况都是最容易挂掉的地方。建议你在平时刷题时就养成一个习惯拿到题先想清楚边界写完之后再用边界测试数据过一次代码。这个习惯不只能帮你应付笔试题对以后的面试手写代码和实际工作都有价值。5. 试卷之外的思考这类笔试背后真正想要的能力5.1 “能不能直接干活”是核心判断标准用友这类公司招人核心判断标准其实就一句话你能不能直接上手干活。企业管理软件的业务逻辑复杂财务核算、供应链、人力管理这些模块背后都有深厚的领域知识公司没那么多时间从头培养一个完全不懂业务也不懂技术的毕业生。所以笔试题目会尽量贴近实际工作场景考的不是你背了多少面试题而是你有没有真正写过业务代码、有没有真正调过SQL、有没有处理过并发问题。理解了这个底层逻辑你就知道复习方向该怎么调整了。Java基础、SQL、设计模式、代码规范这些是日常开发的高频知识点必须花大量时间夯实。我在实际面试候选人时见过不少简历上写着精通Java的结果让他手写一个简单SQL都写不对这就很容易暴露真实水平。5.2 草稿纸上的思考过程也是得分点还有一个容易被忽略的点纸上答题或者在系统里答主观题时你的思考过程其实也是加分项。很多综合题不只有一个标准答案而是看你的答案有没有逻辑、能不能自圆其说。比如设计一个类不同人可能给出不同设计只要类职责清晰、符合设计原则、能解决业务问题都是好答案。所以遇到开放性的设计题不要写两行就交卷。建议先理清需求列出几个关键类画出它们之间的关系再写核心代码。这些额外的思考展示会让阅卷人觉得你不仅有代码能力还有系统设计的意识。这种能力在未来的实际工作中比背一百道面试题重要得多。5.3 这套题为什么会流传下来一个很有意思的现象是“用友2017秋招笔试题”这种题目在各大论坛和面经网站上流传很多年。原因不只是因为用友名气大更是因为这类笔试题目比较稳定、有代表性它们反映了一类企业软件公司的普遍用人标准。即使到现在这套题里的很多知识点依然是高频考点。你在准备时与其到处搜罗各种版本的“最新题库”不如静下心来把这类经典笔试题吃透把背后的知识点和解题思路融会贯通以不变应万变。我自己带过的实习生里有一个对这套经典题刷得特别认真的后来笔试通过进入了一家知名的ERP企业。他跟我聊过说最大的收获不是记住了题目本身而是通过反复练习把Java基础、SQL和代码规范这些基本功真正练扎实了笔试的时候心里有底手上有货自然不慌。所以如果你正在为这类公司的校招笔试发愁别焦虑拿起一套经典的笔试题认认真真做一遍把每一道题的来龙去脉搞清楚。刷题不在多在于把一道题真正吃透能举一反三。等你把这里面的逻辑都摸透了那些五花八门的题目在你眼里无非是同一批知识点的不同组合而已。