news 2026/9/1 20:19:35

用友Java笔试真题解析:从String到JVM与Spring核心考点

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用友Java笔试真题解析:从String到JVM与Spring核心考点

1. 这套题背后的出题逻辑:用友秋招Java笔试到底在考什么

前两天整理硬盘,翻出了自己当年秋招时存的一份用友2018秋招Java笔试题,第六套。重看一遍感触挺深——用友这类传统软件大厂的笔试风格,和互联网大厂的出题思路确实不太一样。互联网大厂喜欢考算法题、系统设计,恨不得让你现场手写一个LRU Cache;用友的卷子更偏“地基”考察,Java基础、集合框架、JVM内存、数据库、Spring这些范围扎扎实实过一遍。说白了,他们要的不是竞赛选手,而是能直接上手做企业级应用开发的工程师。

很多准备秋招的同学容易犯一个错误:把精力全砸在刷LeetCode上,Java基础靠“八股文”临时背一背。等真上了笔试考场,碰到用友这类偏基础的卷子反而懵了——题目看着都眼熟,但就是拿不准。这种感觉我太懂了,当年我自己也是这样,刷了三个月算法题,结果在一道HashMap扩容相关的选择题上栽了跟头。

这篇文章我就把这份第六套笔试题里最有代表性的几类题目拿出来,逐题拆解背后的原理和答题思路。不光是给答案,更重要的是帮你建立一套“看到题目就知道出题人在考什么”的反射能力。无论你是准备秋招的在校生,还是打算跳槽去传统软件企业的Java开发,这套题的复习价值都相当高。后面我还会聊一些关于笔试时间分配和复习策略的实操经验,都是我当年踩过坑之后总结出来的。

2. 从这套题看用友Java笔试的考点分布与复习权重

在看具体题目之前,先整体盘点一下这套卷子的结构。用友的笔试通常分三块:选择题、简答题/编程题、数据库与框架综合题。时长大概90到120分钟,题量不算小,时间其实挺紧的。我当年考的时候,光选择题就有30道左右,涵盖Java基础语法、集合、多线程、JVM、异常处理、IO、设计模式等,覆盖面非常广。

把这套题和同期其他大厂的笔试题对比一下就能发现规律:

考察模块用友2018秋招笔试题(六)占比常见互联网大厂占比考察侧重
Java基础语法与面向对象约30%约15%语法细节、重载重写、内部类、String
集合框架约20%约15%源码逻辑、扩容机制、线程安全
JVM与内存约15%约10%内存区域、垃圾回收、类加载
多线程与并发约10%约20%锁机制、线程池参数
数据库/SQL约15%约10%索引、事务、SQL编写
框架/设计模式约10%约10%Spring核心思想、单例、工厂
算法与数据结构少量约30%排序、链表、二叉树

看出区别没有?用友的卷子明显更看重Java语言本身的功底。这其实反映了传统软件企业的用人逻辑:业务系统生命周期长、维护成本高,他们需要的是对语言底层机制有扎实理解的开发,而不是只会调框架API的人。这套题里甚至有一道关于String s = new String("abc")创建了几个对象的题,这种题互联网大厂已经很少考了,但在传统软件企业的笔试题里依然是常客。

所以准备这类笔试,复习重心和刷互联网大厂是完全不一样的。我建议时间分配上,Java基础、集合、JVM这三块加起来至少要占到总复习时间的60%。框架方面不用太深,Spring的IOC和AOP思想、MyBatis的基础用法搞明白就能应付大部分题目。MySQL的索引和事务属于必考项,但难度通常不会太大。算法题准备常见的那几个就够了,排序、链表反转、二叉树遍历这几类覆盖了绝大多数情况。

3. 选择题中最容易翻车的四个方向:从String到线程安全

这套卷子的选择题里,有些题错得特别冤枉。不是不会,而是没搞懂出题人设的陷阱。我挑四个代表性方向展开讲讲,这些也是Java笔试中的高频易错点。

3.1 String相关题目:别再死记“创建了几个对象”了

先看这道典型的题:

String s1 = new String("abc"); String s2 = "abc"; String s3 = s2.intern(); System.out.println(s1 == s2); // false System.out.println(s2 == s3); // true System.out.println(s1.intern() == s2); // true

考的是String常量池和对象引用比较。很多人看到new String("abc")就直接背答案“创建了两个对象”,但没弄明白为什么,换个问法就懵了。

实际上整个过程是这样的:先看常量池里有没有"abc",没有就在常量池创建一份;同时new关键字在堆上又创建了一个String对象,所以是“一个常量池对象+一个堆对象”。注意这里的顺序和JVM规范实现有关,HotSpot里字符串常量池在JDK 7之后移到了堆中,但逻辑上它和普通堆对象还是分开维护的。

s1 == s2为什么是false?因为一个是堆上new出来的对象引用,一个是常量池里的对象引用,内存地址不同。s2 == s3为什么是true?intern()方法会去常量池找equals相等的字符串,找到了就直接返回常量池引用,所以和s2指向同一个对象。

这套题的核心逻辑就是:==比较的是引用地址,equals比较的是内容。面试官拿这个考察你对JVM对象创建机制的理解,而不是背答案的能力。我建议准备时把“字符串常量池”、“new与直接赋值的区别”、“intern()的实现逻辑”这三块一起看,搞懂原理比背一万道题都管用。

3.2 HashMap的扩容机制:版本差异是区分度所在

接下来是Java面试里的常青树——HashMap。这套题考了一道关于扩容的:HashMap默认初始容量是多少?负载因子是多少?什么时候触发扩容?

基础答案是:默认容量16,负载因子0.75,当size > capacity * loadFactor时触发扩容,扩容后容量翻倍。这道题大多数人能答对,丢分的是后面那道追问:JDK 8里HashMap底层做了哪些改变?

这里值得展开说清楚。JDK 7的HashMap是“数组+链表”,新元素插入采用头插法,并发扩容时会形成环形链表导致死循环。JDK 8改成“数组+链表+红黑树”,链表长度达到8且数组容量达到64时转化为红黑树,插入改成尾插法,解决了一部分并发问题。但要注意,这不代表JDK 8的HashMap线程安全——并发put仍然可能丢数据,只是不像之前那样直接死循环挂掉。

还有个小考点:链表转红黑树的阈值为什么是8?因为源码注释里说了,理想情况下随机hashCode的碰撞概率服从泊松分布,负载因子0.75的情况下,链表长度到8的概率已经降到千万分之一以下。这个细节如果你能答出来,面试官印象分会明显提升。

3.3 多线程基础:从synchronized问到volatile

用友这套题里线程相关的不算特别难,但覆盖面很全。有一道题考synchronized修饰静态方法和实例方法的区别,这道题的得分率据说不高。

关键点在于:synchronized修饰静态方法锁的是Class对象,修饰实例方法锁的是当前实例对象。所以两个线程一个调静态方法、一个调实例方法,它们锁的对象不同,可以同时执行,不存在互斥关系。很多人在这个地方理解成了“只要加了synchronized就一定会互斥”,这种理解是不完整的。

还有一道考volatile的:它能保证原子性吗?标准答案是不能volatile只保证可见性和有序性(禁止指令重排),不保证原子性。经典的例子就是volatile int countcount++操作,这个操作是“读取-修改-写入”三步,两个线程同时执行时仍然会丢更新。这道题出题人很狡猾,选项里有个“volatile可以替代synchronized保证线程安全”,当时我差点就选了这个。

3.4 异常处理:finally里return的陷阱

最后说一个很多人在笔试里掉过的坑——try-catch-finally中的return行为。这套题出了一道很经典的代码阅读题:

public static int test() { int a = 1; try { a = 2; return a; } finally { a = 3; } } public static int test2() { int a = 1; try { a = 2; return a; } finally { a = 3; return a; } }

test()返回2,test2()返回3。很多人不理解:finally不是一定会执行吗?为什么test()返回的不是3?

原理是这样的:当try块执行到return a时,JVM会先把a当前的值(也就是2)保存到操作数栈或者局部变量表中,记录为返回值,然后跳去执行finally块。finally块里修改了a的值为3,但这个修改不会影响已经保存的返回值。所以test()返回的是2。

test2()就不同了,finally块里直接写了个return a,这个return会覆盖之前的返回值,所以结果变成3。更深一层,如果finally块里抛了异常,那异常会覆盖try块里的返回值或者异常。这也是为什么阿里的Java开发手册里明确禁止在finally块中使用return——会导致返回值被覆盖,而且让代码逻辑变得极难排查。

这种题光背结论没用,得理解JVM的字节码执行流程。我建议备考时打开IDE实际跑一遍,然后配合javap -c看字节码,原理一下就能透。

4. 编程与设计类题目:手写排序、单例与Lambda的实战变形

选择判断之外,这套卷子还有几道手写编程题。这部分在试卷中占比不算大,但区分度很高。我挑了三个有代表性的:手写快速排序、线程安全的单例模式、以及一个Lambda表达式相关的代码阅读题。

4.1 快速排序的手写要点:不是背模板就行

题目是“用Java实现快速排序,并说明时间复杂度和空间复杂度”。很多人觉得这题送分,其实不然。能写对是一回事,能写出让面试官满意的实现是另一回事。

快速排序的核心逻辑就三步:选基准、分区、递归。但有几个细节是区分度所在。第一,基准怎么选?我当年的标准写法是选最右边的元素作为基准(Lomuto分区法),这样代码最简洁。但更好的做法是随机选基准或用三数取中法,能有效避免数组基本有序时退化成O(n²)。第二,分区时要注意边界条件,left <= right还是left < right没写对会导致死循环或漏排。第三,空间复杂度很多人答错,都以为是O(1),实际上递归调用有栈空间,平均O(log n),最坏O(n)。

下面是我当时在答卷上写的版本,你可以参考:

public class QuickSort { public void quickSort(int[] arr, int left, int right) { if (left >= right) return; int pivotIndex = partition(arr, left, right); quickSort(arr, left, pivotIndex - 1); quickSort(arr, pivotIndex + 1, right); } private int partition(int[] arr, int left, int right) { int pivot = arr[right]; int i = left; for (int j = left; j < right; j++) { if (arr[j] < pivot) { swap(arr, i, j); i++; } } swap(arr, i, right); return i; } private void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } }

笔试时间紧张,这样写是够用的。但如果时间充裕,我建议面试时额外提一下“最坏情况什么时候出现、如何优化”,这才是展示深度的机会。

4.2 单例模式:从懒汉到双检锁的完整演进

用友这套卷子里的设计模式题出了个很经典的变形:写一个线程安全的单例模式实现。这题的精髓在于你要能给面试官展示出“你是真的理解线程安全,而不是只会背代码”。

我见过太多人直接写双检锁(Double-Checked Locking),但忘记加volatile。这题有两个隐藏考点:为什么双重检查还需要volatile?因为instance = new Singleton()这行代码并不是原子操作,它分三步——分配内存、初始化对象、将引用指向内存。如果不加volatile,JVM可能重排成“先赋值引用后初始化对象”,另一个线程就会拿到一个未初始化完成的对象。

我当时在答卷上用了静态内部类方案,代码简洁而且天然线程安全:

public class Singleton { private Singleton() {} private static class Holder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } }

静态内部类的原理是:Holder类只有在getInstance()第一次被调用时才会被JVM加载,类加载过程由JVM保证线程安全,所以既实现了懒加载又不用同步锁。这是我认为单例模式里最优雅的实现方式。如果你面试时能把这个原理讲清楚,基本可以秒杀大部分候选人。

4.3 Lambda表达式:传统软件企业也开始考了

有意思的是,这套2018年的卷子里已经开始出现Lambda表达式和函数式接口的题目了。有一道题是:

List<String> list = Arrays.asList("banana", "apple", "orange"); list.stream() .filter(s -> s.startsWith("a")) .map(String::toUpperCase) .forEach(System.out::println);

问输出是什么?答案是APPLE

这道题考察的是Stream API的基础操作链:filter表示过滤,保留以"a"开头的字符串;map做转换,把小写转大写;forEach遍历输出。Lambda表达式本身只是一个语法糖,但很多老员工写了几年Java也没真正用明白Stream,导致代码全是嵌套for循环。笔试考这个,其实是在考察你的代码风格是否跟得上现代Java的演进。

我建议这部分的准备不只是会看,还要会写。笔试的改卷老师如果看到你能用Stream流畅地写出集合操作,而不是满屏的for+if,印象分会高不少。

5. 数据库与框架考察:SQL性能优化与Spring核心思想

用友作为老牌企业管理软件厂商,数据库的功底是必考的。这套卷子的数据库和框架题目难度适中,但覆盖面挺广,我挑几个典型的题目来分析。

5.1 SQL题:从索引失效到查询优化

有一道SQL优化题是这样的:表employee有10万条数据,字段name上建了普通索引,查询语句是SELECT * FROM employee WHERE name LIKE '%张%';,问这条语句能否用到索引?

答案是用不到。因为LIKE以通配符%开头的查询,会导致索引失效,走全表扫描。这里背后涉及B+树索引的最左前缀匹配原则。如果改成name LIKE '张%',因为左边是确定的字符,就可以走索引查询。

还有一道题考到了联合索引的最左前缀原则,字段顺序是(department_id, age, salary),问哪些查询能用到索引。答案是:条件是department_iddepartment_id + agedepartment_id + age + salary的查询能用上;直接查agesalary不行,跳过第一个字段就失效了。这个知识点一定要掌握,几乎是每一家笔试都会考的SQL优化内容。

5.2 事务隔离级别:用友的题目引出的知识点

事务隔离级别这道题我做错了。当时记忆不深,只记了四个隔离级别的名字,结果题目问的是“MySQL Innodb默认的隔离级别是什么?它的特点是什么?”我答了可重复读,但对“当前读”和“快照读”的区别一头雾水。

其实可重复读隔离级别下,普通SELECT是快照读,基于MVCC机制,不用加锁;但SELECT ... FOR UPDATEUPDATEDELETE这些是当前读,需要加锁。这也是为什么可重复读级别下可能会出现幻读现象——快照读看不到新插入的行,但当前读能看到。InnoDB通过间隙锁(Gap Lock)来解决这个场景下的幻读问题,这是个很容易被忽略的考点。

我当时在这道题上失分,本质原因是没有把“隔离级别的定义”和“InnoDB的实现机制”联系起来。备考事务相关知识点时,建议把脏读、不可重复读、幻读这三个问题的定义和例子梳理清楚,再去理解四个隔离级别分别解决了哪个问题。

5.3 Spring框架:从IOC/AOP到事务传播行为

框架方面的题目不算太偏,但有一套题很能反映企业实际开发需求:Spring的@Transactional事务在什么情况下会失效?

这是个经典的进阶问题。答案有几种:方法标记为private/final、方法内部自调用、异常被捕获后没有抛出RuntimeException、事务方法被另一个类不同线程调用、数据库引擎不支持事务。其中最容易踩坑的是“方法内部自调用”,写了this.method()而不是通过Spring代理调用,导致事务不生效。这道题用友的卷子里出现了,我记得当时是作为不确定选项出现的,但我后来在实际开发中真的踩过这个坑——一个部门批量导入的方法,明明加了@Transactional,数据插入一半时报错,结果前一半数据没有回滚,排查半天才发现是同类内部方法调用,代理没生效。

如果你在为Java面试做准备,我强烈建议把“事务失效的几种场景”整理成笔记,因为这是从笔试到面试、再到实际开发都会被反复问到的知识点。Spring bean默认是单例的,AOP基于代理实现,原理层面搞懂了,事务失效问题就迎刃而解。

6. 从这套笔试题延伸出来的面试加分项:JVM调参与类加载

这套用友笔试题中JVM相关题目占了不小比例,而且我发现它们不只是考查死概念,而是把概念和实际场景结合。比如有一道题是“当线上应用抛出OutOfMemoryError时,你如何排查?”这题看起来是简答题,其实已经接近真实工作场景了。

6.1 JVM内存区域:从笔试到排查问题的思路

准备这类题,首先要把JVM运行时数据区的划分搞清楚:程序计数器、虚拟机栈、本地方法栈、堆、方法区(JDK 8之后是元空间)。笔试大概率会考“哪些区域会抛出OutOfMemoryError”,答案是除了程序计数器外,其余区域都可能。栈溢出抛的是StackOverflowError,堆和元空间溢出抛的是OutOfMemoryError

但真正拉开差距的是后面那道实操题:线上应用内存溢出,你怎么办?我当时列了一个排查顺序:先看启动参数确认堆大小,再用jps找到进程号,用jmap -heap看堆内存使用情况,jmap -dump导出堆转储文件,最后用MAT或者JProfiler分析大对象和泄漏链。如果操作系统层面内存也接近耗尽,那就用topps结合jstat看看GC频率和回收时间,确认是内存泄漏还是内存不足。

这套排查逻辑其实任何一本JVM书里都有,但笔试能写得完整的人不多。我建议准备时把命令和排查顺序背熟,同时理解每个步骤的目的——这样做,不管题目怎么变形,你都能从容应对。

6.2 类加载机制:双亲委派模型的实际意义

类加载题也值得好好准备,用友这套题问的是“什么是双亲委派模型?为什么这样设计?”这个问题我总结成一个核心答案:避免核心类库被篡改,让类加载具有层级关系,保证Java类型体系的安全。具体来说,一个类加载器收到加载请求时,不会自己先加载,而是先委托给父加载器,逐级向上,只有当父加载器无法完成时才自己加载。

笔试里常见的扩展考点是:能不能自己写一个java.lang.String类并替换JDK的版本?答案是“可以编译,但不会被加载”,因为双亲委派机制保证任何自定义的java.lang.String最终都会交由启动类加载器(Bootstrap ClassLoader)加载JDK自带的String类。还有ClassCastException类加载器冲突的问题:同一个类若被不同类加载器加载,就会被视为不同的类,强转会抛异常。这个问题在热部署、模块化场景下非常常见。

我在准备这部分时,发现一个很有效的学习方法:自己写一个类加载器,自定义findClass逻辑加载磁盘上的class文件,跑一遍之后对双亲委派的理解会有质的提升。单纯背概念的记忆留存率非常低。

7. 秋招笔试的应试策略:时间分配与复盘方法

讲完具体的考点,最后聊点应试层面的经验。这部分是很多备考攻略不会告诉你的,但恰恰是最影响得分率的。

7.1 时间分配的三个优先级

当时我做这套题的时候,时长90分钟,选择填空判断花了差不多30分钟,编程题和SQL题花了50分钟,最后留了10分钟检查。这个时间分配比较合理,但前提是选择题一定要果断,不能在一道题上卡太久。我的经验规则是:一道选择题如果30秒内没有明确思路,就先标记跳过,等到做完所有题目后再回头纠结。笔试的得分率比单题正确率重要得多,为了一道两分的选择题耗费十分钟,后面的编程题很可能做不完。

做题顺序上,我建议先做编程题和SQL题,再做选择题。理由很简单:编程题分值高、需要清醒的头脑做大题的逻辑推演,放在最后只会写得粗糙甚至超时。选择题就算时间紧张,蒙对的概率也不低。当然这个顺序因人而异,如果你选择题基础非常扎实,也可以先做选择热身再攻大题,但一定要控制时间。

7.2 错题复盘的三个层次

笔试后的复盘比刷题本身重要十倍。我用友这套题做完对完答案,把错题分成了三类:纯记忆型、原理理解型、场景应用型。记忆型的比如HashMap初始容量几、RMI和RPC的区别,这种整理成表格每天过一遍就行。原理理解型的比如volatile为什么不保证原子性、快排最坏情况为什么退化成O(n²),这种需要深挖底层机制,用画图或者写Demo的方式理解透。场景应用型的比如线上OOM怎么排查、事务失效的场景,这类题是面试的高频题,一定要结合真实项目经历来理解和讲述。

我的复盘习惯是建立一个错题溯源表,每道错题都追到具体的知识模块,然后标记“是因为概念模糊、理解偏差,还是纯没记住”。比如同样是String相关的题,如果错在==equals上,就说明基础概念没打通;如果错在intern()的返回值上,就说明对常量池的具体实现不熟。这样分析之后,后续复习就能直击要害。

7.3 如何利用笔试题反推企业技术栈

还有一个额外的价值:笔试题目往往能反向暴露企业的技术栈和招聘偏好。用友这套题里数据库和SQL优化占比高、有大量Spring相关题目,说明他们后端业务对数据库依赖很深,开发模式偏SSH/Spring Boot这类传统企业框架。如果你后续要去面试,就可以针对性地准备这些方向的项目案例。这种分析思路对每个人做求职匹配都有帮助——先在企业笔试中看出他们关注什么技术栈,再决定要不要针对性补充哪方面的项目经验,比盲目海投效率高很多。

8. 这套笔试题的核心命题视角回顾

以一个过来人的角度把这份用友2018秋招Java笔试题(六)复盘完,最大的感受是:它的每题几乎都指向一个很明确的人才画像——你不需要是精通分布式架构的专家,但你必须把Java这门语言本身吃透,把企业级开发的常用生态搞明白。

这套卷子从String对象创建到JVM内存划分,从HashMap扩容到单例模式的多种写法,从SQL索引优化到Spring事务传播机制,涵盖的正是Java后端开发每天都会接触的基础面。如果你能把这些题背后的原理都理清,做题时不会有“某个答案看起来都对”的模糊感,而是每一道题都能对应到明确的知识树分支。

还有一点想特别提醒:别小看这些“基础题”。我后来在面试候选人时发现,能流利说出HashMap的底层实现细节的人不少,但能在实际业务场景中合理使用它、知道什么时候该选ConcurrentHashMap、什么时候该换TreeMap的人却不多。笔试只是第一关,真正的分水岭是能不能把基础机制和工程实践融会贯通。这套题的价值在于,它给了你一个很清晰的检查清单,让你知道自己的基础是否配得上你想去的那个岗位。

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

MySQL按中文排序:ORDER BY遇到中文乱序怎么办?5种方案

做后台开发的同学应该都碰到过这个场景&#xff1a;页面上有个下拉列表或者表格&#xff0c;需要按中文姓名、城市名排序显示&#xff0c;结果一查出来&#xff0c;张三排到李四前面还是后面完全看运气&#xff0c;搞得产品经理天天追着你问"这个排序怎么是乱的"。My…

作者头像 李华
网站建设 2026/9/1 20:11:41

Sentinel实战:微服务限流、熔断与降级的核心原理与落地

先说结论&#xff1a;Sentinel 是面向微服务、分布式系统的流量治理组件&#xff0c;核心就三件事&#xff1a;限流、熔断降级、系统保护。微服务里真正让人头疼的不是功能开发&#xff0c;而是流量一上来、下游一慢、某个接口一抖动&#xff0c;整个链路跟着挂。很多人把“限流…

作者头像 李华
网站建设 2026/9/1 20:10:08

深入理解POSIX:编写跨平台Shell脚本的兼容性指南

各位读者朋友&#xff0c;大家好。之前在实际开发中&#xff0c;经常遇到这样一个场景&#xff1a;同一份 Shell 脚本&#xff0c;在一台 Ubuntu 服务器上运行得好好的&#xff0c;换到本机 macOS 终端里就报错&#xff0c;或者输出结果对不上。网上搜了一圈&#xff0c;答案零…

作者头像 李华
网站建设 2026/9/1 20:07:27

一个 Java 工程师的 28 天 Python 之旅:从“复制粘贴“到自己的作品

我是一个写了多年 Java 的后端工程师&#xff0c;动手能力不算强&#xff0c;学东西主要靠模仿和记忆。这篇文章记录我用 28 天学完 Python 全栈&#xff08;数据处理 → Web 接口 → 数据库 → 页面 → AI 模型&#xff09;的完整过程&#xff0c;包括第 20 天差点弃坑的迷茫、…

作者头像 李华
网站建设 2026/9/1 20:04:39

1.web记录

1.js数据类型基本数据类型&#xff1a;string boolean number undefined null symbol bigInt引用数据类型:Object(对象 数组 函数)2.怎么去判断数据类型方法一&#xff1a;typeof 不能判断 null、Array、Object方法二&#xff1a;instanceof 判断Object 不能判断 null、undef…

作者头像 李华