尧图精选

2020秋招奇安信Java笔试题解析:从基础到安全编码全覆盖

🕒 发布时间:2026/9/1 17:12:58 📁 来源:尧图网络
2020年秋招的Java笔试题现在回看依然很有参考价值。奇安信这份Java方向试卷2我当时做完最大的感受是常规知识点占了七成但真正拉开分差的是那三成带有安全思维烙印的题目。如果你准备的是网络安全类企业的Java岗这套卷子值得好好研究如果你想检验自己的Java基础扎不扎实它也是一面很准的镜子。为什么这么说因为安全公司的Java开发不光是写业务、调接口还要写出的代码本身能扛住攻击。所以它的考题里除了java面试八股文里常出现的HashMap、线程池、JVM还会额外考察Web漏洞原理、加密算法、安全编码习惯这类内容。这个组合在一般互联网大厂里不太常见在安全企业的校招里却是常态。下面我按照试卷的考点拆开讲顺便把每类题的答题思路和容易踩的坑也一并说清楚。1. 试卷整体设计与考点拆解1.1 安全企业Java岗的笔试风格是什么样的先说结论奇安信的Java笔试题整体难度在同类企业里属于中上但题目本身不偏不怪绝大多数考点都在考纲范围内。真正需要留意的是它的出题角度。大部分互联网公司的Java试卷核心是数据结构、Java基础、数据库、框架安全相关内容最多考一道概念题。但奇安信这种安全背景的公司出题思路上会把安全编码当成基本素质来考。比如同样问你SQL语句怎么写普通公司会考预编译Statement的用法奇安信可能会再问一句为什么这种方式能防止SQL注入。同样考察输入验证它可能直接给你一段有路径遍历风险的代码让你找出问题。这个特点决定了复习方向不能只背java面试题。你需要把知识点理解到能讲出原理的深度尤其是那些和安全沾边的部分比如字符串处理、文件操作、序列化、加密算法。对通过笔试来说理解原理比死记硬背结论重要得多。1.2 2020年Java技术栈的命题背景2020年的时候Java 8还是绝对主流Spring Boot 2.x在企业里大面积铺开微服务和分布式正处于快速普及期。JDK 11其实已经发布但生产环境用得少大部分公司的技术栈还停留在Java 8。这套试卷的命题背景就是Java 8。所以你会发现这一年的Java面试题目里不会出现Records、Sealed Class、var这类新语法反而会重点考Java 8引入的东西Stream、Lambda、Optional、接口默认方法。如果现在复习很多人习惯用JDK 17甚至JDK 21的视角去理解Java这没问题但要做企业笔试题还是要先把Java 8的使用和原理吃透。另一个背景是2020年正好是国内网络安全产业快速扩张的时期安全企业对研发人才的需求量大笔试题目也趋于体系化。试卷2这种命名方式说明题目是成套出现的考点有意识地覆盖了多个维度不是一个老师随手出的零散题而是想通过一张卷子筛选出基础扎实、有安全素养的候选人。1.3 考点分布与分值占比我把这套卷子涉及的主要考点整理成了表方便对照复习考点方向常见考察内容大致占比Java基础与集合框架String、HashMap、ArrayList、泛型、异常处理25% - 30%并发编程synchronized、volatile、锁、线程池、死锁15% - 20%JVM内存区域、GC算法、类加载机制、OOM排查10% - 15%数据结构与算法排序、链表、数组、复杂度分析20% - 25%数据库与框架MySQL索引、事务、Spring IoC/AOP10%安全特色知识Web漏洞、加密算法、安全编码规范10% - 15%可以看出Java基础和算法是重头并发和JVM是拉开差距的地方安全特色知识反而是性价比最高的部分。很多人对安全考点没概念看到题就慌其实这些题目考得很基础只要花几天补一下知识就能拿分。2. Java核心基础与集合框架类题目2.1 String类看似送分实则处处是坑String是Java笔试里出现频率最高的类没有之一。奇安信试卷2里围绕String出的题基本上覆盖了三个经典问题String为什么不可变、StringBuilder和StringBuffer的区别、字符串常量池与intern方法。String不可变是因为它的核心字符数组value被final修饰并且在构造之后没有任何方法能修改这个数组。不可变带来的好处是线程安全、可以做字符串常量池缓存、可以在HashMap中安全地作为key。这个点我建议从源码层面回答把value、final、常量池三者之间的关系讲清楚而不是简单背一句因为final。另一个高频题是new String(abc)到底创建了几个对象。答案是两个一个是编译期在常量池中创建的abc字面量一个是运行期在堆内存中创建的String对象。如果常量池中已经有abc那就只创建一个堆对象。这个我建议画个内存图帮助理解笔试虽然不用画图但自己理清了答题才不会含糊。StringBuilder和StringBuffer的区别核心在于StringBuffer的方法用synchronized修饰了线程安全但性能稍差StringBuilder是线程不安全的但单线程环境下性能更好。实际开发中单线程拼接字符串用StringBuilder并发场景很少需要共享一个可变字符串所以StringBuffer用得反而不多。这个点答的时候别只背结论最好举一个实际拼接SQL或者JSON的场景来说明取舍。2.2 HashMap从应用到原理一条线都能问到HashMap几乎是所有Java面试的必考题奇安信这种安全背景的公司问得还会更细。常见问题包括底层数据结构、put和get的流程、扩容机制、为什么链表转红黑树的阈值是8、HashMap为什么线程不安全。JDK 8里的HashMap底层是数组加链表加红黑树。put一个key时先计算key的hashCode再进行一次扰动处理然后定位到数组下标。如果该位置为空就直接放入不为空就用equals方法比较key相同就覆盖不同就挂在链表后面。链表长度超过8且数组容量大于等于64时链表转成红黑树。扩容方面默认初始容量是16加载因子是0.75。当元素数量超过容量乘以加载因子的结果时触发扩容容量翻倍。扩容后元素的位置要么在原下标要么在原下标加上原容量的位置。这个规律是JDK 8优化过的目的是让rehash过程更高效这也是2020年真题里常考的一个细节。关于线程不安全JDK 7里并发put可能产生环形链表导致下次get出现死循环JDK 8修复了这个问题但并发put仍可能丢数据。答这道题时如果能答出JDK 7和JDK 8的区别会显得你的理解更深一层。安全公司问HashMap还有一个隐藏用意如果一个对象作为key参与hash计算但设计得不好hash值分布不均匀就会引发大量哈希碰撞被人为构造恶意数据时甚至能造成拒绝服务攻击这在Web场景里是一个真实的安全风险。2.3 ArrayList、LinkedList与fail-fast机制ArrayList和LinkedList的区别是基础题但试卷里一般不直接问你区别而是包装成场景题比如频繁在列表中间插入删除用哪个更合适或者遍历大列表时用哪种方式性能更好。ArrayList底层是动态数组随机访问快插入和删除慢因为需要移动元素。LinkedList底层是双向链表插入删除快但随机访问慢因为要遍历查找。遍历的时候LinkedList用迭代器的getNext方式比for循环加get(i)方式快很多因为每次get(i)都要从头开始找。这个细节在笔试里很容易被忽略。fail-fast机制也是一个考点。ArrayList在迭代过程中如果通过add或remove方法修改了列表结构迭代器会抛出ConcurrentModificationException。原因是迭代器内部维护了一个modCount每次修改列表结构时modCount加一迭代时如果发现modCount变了就抛异常。但需要注意迭代器自己的remove方法不会抛异常因为它会同步修改expectedModCount。举这个例子最好的方式是写一小段代码演示抛出异常的场景然后解释原因。3. 并发与JVM真正拉开差距的地方3.1 synchronized和ReentrantLock怎么选才加分并发是校招Java方向的试金石奇安信试卷2在这个部分出的题基本围绕Java并发的基础设施展开synchronized、volatile、锁机制、线程池、死锁。synchronized和ReentrantLock的对比是经典中的经典。synchronized是JVM层面的内置锁通过字节码指令monitorenter和monitorexit实现使用简单出了异常会自动释放锁。ReentrantLock是java.util.concurrent包下的API锁需要手动加锁和解锁但提供了更灵活的能力可中断、可超时可以让一个线程在等待锁的时候响应中断支持公平锁可以在构造时指定避免线程饥饿可以绑定多个Condition实现精准唤醒。从JDK 6开始synchronized经历了锁升级的过程无锁、偏向锁、轻量级锁、重量级锁。答这个点的时候提一下锁升级会显得有深度。我见过不少考生把synchronized和ReentrantLock的区别背得很熟但一问锁升级就懵了。其实锁升级的实质是耗时操作优先用非阻塞方式尝试不行再升级到阻塞这是性能和可控性的权衡。3.2 volatile和内存可见性的真正含义volatile在并发题里的地位仅次于synchronized。两个核心作用保证可见性、禁止指令重排但不保证原子性。可见性指的是一个线程修改了变量其他线程能够立刻看到。这涉及到Java内存模型中的主内存和工作内存概念。每个线程有自己的工作内存变量在这份副本上操作volatile强制对这个变量的读写都直接基于主内存。禁止指令重排则是有序性问题的解决方案JVM为了优化指令可能改变代码的执行顺序volatile通过内存屏障阻止了这种重排。典型的应用场景就是双检锁单例。为什么单例对象要用volatile修饰因为new一个对象分三步分配内存、初始化对象、将引用指向内存。如果不加volatileJVM可能把第二步和第三步重排导致另一个线程拿到一个未初始化完成的对象引用。我用生活化类比解释一下这就好比网购一个组装家具快递先送来一个没组装好的半成品包装你急着打开组装结果发现缺零件。volatile的作用是保证零件到位这一步必然先完成。3.3 线程池参数背后的设计逻辑线程池几乎是每套Java面试题必出的内容。核心问题是ThreadPoolExecutor的七个参数和四种拒绝策略。七个参数分别是核心线程数、最大线程数、空闲线程存活时间、时间单位、任务队列、线程工厂、拒绝策略。这里面有两个容易混淆的点。第一当提交任务时先判断当前线程数是否小于核心线程数小于则创建新线程不小于则把任务丢进队列队列满了再判断是否小于最大线程数小于则创建临时线程临时线程空闲超过存活时间会被回收。第二线程工厂用默认的就行但阿里规范推荐显式指定线程名称前缀排查日志时一眼能看出是哪个线程池出了问题。四种拒绝策略分别是AbortPolicy抛出异常、CallerRunsPolicy由调用线程执行任务、DiscardPolicy静默丢弃、DiscardOldestPolicy丢弃最旧任务。这个知识点的考察点不在于背下四个名字而在于能根据业务场景选策略。比如异步通知类任务丢一条没关系可以用DiscardPolicy但审计日志这种关键任务宁可阻塞调用方也不能丢应该用CallerRunsPolicy或者自定义策略。3.4 JVM内存区域与OutOfMemoryError的排查思路JVM相关的题2020年的试卷2重点关注内存区域划分、垃圾回收算法、类加载机制。这套内容也是现在Java面试八股文里的常青树。内存区域的核心是堆、虚拟机栈、本地方法栈、方法区、程序计数器。要区分线程共享和线程私有堆和方法区是共享的虚拟机栈、本地方法栈、程序计数器是私有的。Java 8之后方法区被元空间取代使用的是本地内存永久代的OOM问题变成了元空间扩展问题。垃圾回收算法的考察主线是标记-清除、标记-复制、标记-整理。新生代用的是标记-复制因为大量对象朝生夕死复制成本低老年代用标记-清除或标记-整理因为对象存活率高复制不划算。GC Roots包括局部变量引用的对象、静态变量引用的对象、JNI引用对象等可达性分析通过GC Roots向下搜索不可达的对象被标记为可回收。出现OutOfMemoryError时怎么排查这是企业很看重的能力。按经验来先jps找到进程ID再用jmap -heap加上jmap -dump导出堆快照配合MAT或VisualVM分析大对象。如果内存溢出发生在老年代多半是内存泄漏重点看对象的引用链如果是直接内存区域溢出要考虑MetaSpace是否设置过小。热搜词里那些Nginx Java outofmemoryerror insufficient memory之类的问题本质上不是你代码的问题就是系统整体内存不足这要分清楚。3.5 类加载机制与双亲委派模型类加载机制的考点分三步加载、链接、初始化三步中最重要的是加载阶段。加载阶段做的事是找到类文件读取字节流在方法区生成类元信息同时在堆中生成一个Class对象作为访问入口。双亲委派模型是必考内容。它的工作流程是类加载器收到类加载请求后不会自己先加载而是把请求委派给父类加载器逐级向上直到最顶层的启动类加载器。只有父类加载器反馈无法完成加载时子类加载器才会尝试自己加载。为什么需要这个机制两个原因。第一是避免类重复加载同一个类只会被一个加载器加载并统一第二是安全因素防止核心类被篡改比如用户自定义一个java.lang.String双亲委派机制会让启动类加载器加载真正的JDK内置String类从而保证核心库的安全性。这个知识点和安全公司的技术栈天然契合值得花时间深入理解。4. 数据结构与算法题笔试的硬门槛4.1 冒泡排序和快速排序手写代码要拿满分2020年奇安信Java试卷2的算法题排序是高频方向。冒泡排序属于基础中的基础快速排序则能起到筛选作用。这里我把两个排序的Java实现都写出来并给出复杂度分析。冒泡排序的核心思路是相邻元素两两比较大的往后移动。每一轮确定一个最大的元素放到末尾。Java实现如下public static void bubbleSort(int[] arr) { if (arr null || arr.length 2) { return; } int n arr.length; for (int i 0; i n - 1; i) { boolean swapped false; for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { int temp arr[j]; arr[j] arr[j 1]; arr[j 1] temp; swapped true; } } if (!swapped) { break; } } }这里加了个swapped标记如果某一轮没有发生交换说明数组已经有序直接终止。这是冒泡排序的常见优化点笔试时写上会加分。时间复杂度方面最坏是O(n平方)最好O(n)空间复杂度O(1)稳定排序。快速排序用的是分治思想先选一个基准值把数组分成小于基准和大于基准的两部分然后递归处理。Java实现如下public static void quickSort(int[] arr, int left, int right) { if (left right) { return; } int pivot arr[left (right - left) / 2]; int i left; int j right; while (i j) { while (arr[i] pivot) { i; } while (arr[j] pivot) { j--; } if (i j) { int temp arr[i]; arr[i] arr[j]; arr[j] temp; i; j--; } } quickSort(arr, left, j); quickSort(arr, i, right); }笔试时快排容易在边界条件上翻车比如left和right相等的情况或者i和j交错之后递归区间划分错。我建议每次写完后自己用长度为1、2、3的数组各跑一遍逻辑确认没有死循环风险。快速排序平均时间复杂度O(n log n)最坏O(n平方)空间复杂度O(log n)不稳定排序。额外说一句Arrays.sort在元素少时用的是插入排序元素多时用的是双轴快排理解了这个答题时提到的排序性能对比会更有底气。4.2 链表题的快慢指针套路除了排序链表题也是常见考点。比如判断链表是否有环、寻找链表的中间节点、反转链表。这类题目的核心技巧就是快慢指针。判断链表是否有环时快指针每次走两步慢指针每次走一步如果链表存在环两者必然在环内相遇。寻找中间节点时快指针走两步慢指针走一步快指针到达末尾时慢指针正好在中间。反转链表则需要三个指针pre、cur、next逐节点反转指向。在安全公司笔试里链表题还会稍微变个方向比如考单链表逆序输出要求不用栈这本质上还是递归反转的思路。这类问题不复杂但很考验代码熟练度。建议考前把链表的基础操作手写十遍以上形成肌肉记忆。4.3 算法题答题中的边界意识我在评价候选人代码时边界条件处理是我最看重的一点。一道题做对了主流程的人很多但能处理空输入、单元素输入、大数溢出的才是真正的加分项。空指针判断要写数组越界要防字符串输入要处理首尾空格。递归算法要防止栈溢出二分法要防mid计算溢出这可以写成int mid left (right - left) / 2而不是(left right) / 2。大数相乘要考虑用long或者BigInteger。在网络安全场景下尤其要注意处理超长输入或恶意构造的payload算法题的边界意识往深了说就是一种安全编码意识。5. 安全特色题目奇安信试卷2的隐藏重点5.1 为什么安全公司要考安全知识我在前面说奇安信的Java试卷会有三成左右的题目带着安全烙印。有考生不理解Java开发岗又不是渗透测试岗为什么要考Web漏洞原理这里要解释清楚。互联网公司研发的代码最终部署在公网或内网环境会被大量真实流量访问。安全公司的产品更不用说它服务的就是客户的安全需求。一个Java开发如果既不理解SQL注入原理也不知道文件上传功能可能被用来执行恶意脚本写出来的代码就会天生带有漏洞。代码审计工具可以查出部分问题但设计层面的安全问题还是靠开发者的意识。所以奇安信这类企业的面试官会通过笔试直接筛选出具备基本安全常识的候选人。这类知识不需要你达到渗透测试工程师的水平但至少要理解常见攻击是怎么发生的以及为什么在代码层面能够防御。5.2 Web漏洞高频考点SQL注入、XSS、CSRF、路径遍历这张试卷里如果出安全简答题大概率是围绕这几个最经典的Web漏洞SQL注入、XSS、CSRF、路径遍历。SQL注入核心原因是用户输入被直接拼接进了SQL语句。防御方法首选预编译使用PreparedStatement的占位符让SQL结构在编译时固定输入只作为参数传递拼接进来的内容无法改变SQL语义。我在实际开发中一直推荐这条路径因为它不依赖过滤逻辑上彻底。XSS也就是跨站脚本攻击核心原理是用户输入被当作HTML或JavaScript执行。防御方向是输出编码。以Java Web为例前端展示用户输入时用fn:escapeXml或者ESAPI库编码防止
上一篇/下一篇内容由系统自动关联 返回资讯列表 →