
1. 为什么2017年的Java笔试题放到今天仍然值得啃1.1 一份“老卷”背后的筛选逻辑2017年秋天科陆集团面向应届生的Java工程师岗位出了一套笔试题。放在当时看它和绝大多数互联网公司的校招试卷没有本质区别选择题考语法细节问答题考框架理解编程题考手写代码能力。但如今回头再看这套题我发现一个很有意思的现象——Java笔试的核心考点其实已经很多年没有大变过。科陆的主营业务是智能电表、储能系统和能源物联网属于典型的“传统制造业物联网软件”复合型公司。它招Java工程师并不需要你上来就会写分布式中间件也不需要你手撕红黑树。它的试卷瞄准的是三件事基础牢不牢、工程思维有没有、碰上没见过的场景会不会查资料。这也是为什么2017年的试卷放到2025年依然有参考价值——面试官想确认的底层能力没有变。很多准备校招的同学容易陷入一个误区拼命追最新技术栈。今天看到Spring Boot 3出就赶紧学明天看到GraalVM讨论度高又去查结果最基础的HashMap扩容原理却说不清楚。这份2017年的试卷恰恰反映了校招的底层逻辑对于初级工程师公司不指望你什么都会但要求你学过的都扎实。扎实就意味着集合、多线程、JVM、SQL这些老生常谈的领域你必须能扛住“八股文”式的追问。1.2 这些考点现在依然是面试高频我把当年试卷涉及的知识点梳理了一下放到今天的面试场景里逐一对照发现重合度极高2017年考点2025年面试热度说明Java基础语法标识符、运算符、类型转换依然高频选择题和电话面第一轮的最爱面向对象三大特性依然高频从概念题变成了“请你手写一个多态场景”集合框架ArrayList/HashMap热度更高现在几乎必问HashMap底层多线程与synchronized热度更高面试深度从“用过”升级到“实现原理”JVM内存模型与GC热度持平老牌八股文考点长盛不衰冒泡/快速排序热度略降手撕算法的难度已经升级到树和DP了SQL与索引热度持平初级岗依然喜欢考Spring轻量级框架理解热度升高从SSH换成了Spring Boot这不是偶然。Java是一门生态极其庞大、但核心语法非常稳定的语言。十年过去了HashMap的源码逻辑、ConcurrentHashMap的锁粒度设计、JVM的运行时数据区划分这些底层的“道”没有变。变的是上层框架的“术”。所以一份2017年的老试卷反而能帮你剥离掉框架的噪音聚焦到真正核心的Java知识体系上。2. Java基础语法那些“送分题”其实在筛人2.1 标识符、运算符与类型转换科陆这套卷子的选择题部分开头几道基本都是Java语法基础题比如标识符命名规则、运算符优先级、基本类型之间的隐式转换。看起来像是送分题但很多人在这一块反而栽跟头。拿标识符来说校招里至少有三成的人会在这道题上犹豫。Java标识符的规则就三条字母、下划线、美元符号开头后面可以跟数字不能是关键字大小写敏感。看起来简单但题目只要拐个弯比如问int 123abc 1;合不合法或者int true 0;合不合法立刻会有人选错。前者错在数字开头后者错在true是布尔字面量而非关键字——严格来说它不是关键字但它是保留字同样不能用作标识符。这种细节恰恰是笔试想筛掉的“基础不牢”。运算符和表达式这道题也很有代表性。常见考法int a 5; int b a a;最后输出多少很多人一看到自增自减就懵。我建议你记住一个判断方法a是先使用后自增a是先自增后使用。所以a a在a5时先取a的值为5此时a变成6再取a的值a先自增为7表达式值是7所以结果是12a最终是7。这类题没有捷径只能靠多练形成肌肉记忆。类型转换也是高频考点。短整型转整型是隐式转换没问题但float f 3.14;这种写法编译不过因为3.14是double类型直接赋值给float会精度丢失必须写成3.14f。类似的还有byte b 127;合法但byte b 128;直接报错因为超出byte的范围-128~127。笔试卷子不会问“你怎么理解类型转换”而是直接放一串代码让你判断对错考察的就是你写代码时是否踩过这些坑。2.2 异常机制与数组越界一道题看出你写过多少代码有一道让我印象深刻的题配着一段代码尝试访问长度为5的数组的第10个下标。选项里有ArrayIndexOutOfBoundsException、IndexOutOfBoundsException、NullPointerException还有“编译错误”。很多新手选了IndexOutOfBoundsException理由是不管数组还是集合越界都是父类。但面试官想看到的是你对数组越界异常的精确定位能力。数组越界在Java里抛的确实是ArrayIndexOutOfBoundsException它是IndexOutOfBoundsException的子类。如果题目代码操作的是ArrayList那越界抛出的是IndexOutOfBoundsException。这个区别在笔试里可能就是一道选择题但在实际开发排错中却直接影响你查问题的速度。日志里如果只写了IndexOutOfBoundsException你还要再想一下是数组还是List如果直接是ArrayIndexOutOfBoundsException你就可以直接去查所有[]访问的地方排查范围立刻缩小一半。异常这块还有一个隐藏考点就是受检异常和非受检异常的区别。试卷里通常会给出一堆异常类让你归类。IOException、SQLException属于受检异常编译器强制你捕获或抛出NullPointerException、ArithmeticException、ArrayIndexOutOfBoundsException属于运行时异常不用强制处理出问题直接抛。这个知识点难吗不难。但能准确说是受检还是非受检的人至少证明他是真的处理过这些异常而不是只在书上看过。2.3 枚举类型与常用类细节题里的老演员很多人在准备校招时会把枚举类型当成一个“知道就行”的知识点结果科陆当年的试卷里偏偏就给了几道关于枚举的题。比如问enum Color { RED, GREEN, BLUE }最后.values()返回的是什么答案是Color[]数组而不是ListColor。这类题考察的是你有没有真正用过枚举。枚举在Java里不是一个简单的常量集合它本质上是一个继承自java.lang.Enum的类。所以你可以给枚举添加构造器、字段和方法。最经典的用法是用枚举实现单例因为Enum类本身保证了线程安全和序列化安全。面试官如果顺着这道选择题追问经常就是“枚举除了定义常量还有什么实际应用”。如果你能说出状态机、策略模式配合枚举甚至单例模式那这道题就从送分题变成了加分题。常用类方面试卷里出现过Integer的缓存问题。Integer a 127; Integer b 127; a b返回什么答案是true因为Integer缓存了-128~127。但如果赋值为128a b就变成了false因为超出缓存范围会各自new一个新的Integer对象。这个考点几乎年年都有但依然年年有人答错。还有equals和的区别。比较的是引用地址equals默认也是比较地址但String和包装类型都重写了equals所以内容相同就返回true。笔试里经常出这种代码两个内容相同的字符串用比较返回什么用equals比较返回什么。只要记住“比地址equals比内容前提是重写过”这类题就不会错。2.4 环境变量与编译选项笔试之外的隐藏“热身题”说实话当年我拿到科陆这份卷子的时候第一页除了选择题还有一道让我意外的题——关于环境变量配置的。大概是问JAVA_HOME和PATH分别起什么作用。当时我的第一反应是“这不是笔试应该考的吧”但后来在做Java开发的过程中我越来越理解出题人的用意。JAVA_HOME是给构建工具和IDE用的它指向JDK的安装目录PATH是让操作系统在命令行里能找到java和javac。如果你这两者都配置不好说明你根本没法在命令行环境下编译运行Java程序更别提部署服务器上的Java应用了。现在的笔试虽然很少直接考这些但面试时偶尔会冒出一句“你遇到过Error: Could not find or load main class吗”这就是在考察你对类路径和JVM启动机制的理解。还记得热词里有一条“java: 警告: 源发行版 17 需要目标发行版 17”吗这个问题在今天的IDE里太常见了。本质是编译器的--source和--target版本不匹配或者IDE的Java版本和项目配置的JDK版本不一致。解决办法有两种一是把项目pom.xml里的maven.compiler.source和maven.compiler.target调到和本机JDK一致二是在IDE里统一Project Structure中的SDK和Language Level。如果你在笔试现场被问到这类问题最安全的回答思路是“先检查JDK版本和IDE配置再检查Maven或Gradle的编译参数最后确认环境变量JAVA_HOME指向的是不是同一个JDK版本。”这份排查思路比死记硬背答案更有价值因为它背后是“编译链路”的意识。3. 面向对象从概念填空到代码实现的转换3.1 封装、继承、多态笔试里到底怎么考科陆这份试卷里面向对象部分占的比例不低。这类题最经典的考法是给你一段代码里面有继承关系有方法重写然后问你输出什么。比如定义一个父类Animal一个子类Dog运行时调用new Dog().makeSound()问你底层是怎么找到子类方法的。这里真正想考的其实是动态绑定。Java在方法调用时如果方法是实例方法JVM会在运行时根据对象的实际类型来决定调用哪个方法而不是根据引用类型。所以Animal a new Dog(); a.makeSound();只要Dog重写了makeSound()调用的就一定是Dog的实现。这个知识点不难但如果你只背了“方法重写是运行时多态”这句话没有真正理解对象头里的类型指针一旦题目换成“父类引用调用子类新增的方法会怎样”你照样会卡住。答案是编译直接报错因为编译器只认引用类型声明的方法。封装这个概念笔试很少让你解释定义而是让你分析访问修饰符。public、protected、default、private的可见范围是必须背下来的。这里有个大家容易忽略的点protected不只是同包和子类可访问。在不同包下只有通过继承关系才能访问父类的protected成员直接通过父类引用访问是不行的。这个细节经常出现在选择题的陷阱里。3.2 String与字符串常量池笔试必争之地面向对象部分里String类的题目是绝对的主角。科陆当年的试卷里有一道题问String s1 abc; String s2 new String(abc);创建了几个对象。答案是两个——字符串常量池里的abc以及堆内存中new出来的那个String对象。但这个答案有一个前提常量池里之前没有abc。如果之前已经出现过abc那么new String(abc)就只创建一个堆对象。还有个更绕的考点String s3 a b c;创建了几个对象很多人以为每次都会创建一个新对象所以是三个。但Java编译器编译时做了常量折叠a b c会在编译期直接变成abc所以如果常量池里没有abc那就只创建一个对象。但如果两边有变量比如String a a; String b a b;此时a b会在运行时通过StringBuilder拼接相当于创建了一个StringBuilder对象、一个String对象以及拼接时使用的字符数组。也就是说变量参与拼接时比较的结果很可能是false因为不能用常量池判断。这类题考得极多建议你把《深入理解Java虚拟机》里字符串常量池那节反复看几遍再配合几道练习题才能真正掌握。StringBuilder和StringBuffer的对比也是必考。StringBuffer的方法是synchronized修饰的线程安全但性能差StringBuilder非线程安全但性能好。笔试喜欢问“在单线程环境下的字符串拼接应该选哪个”答案是StringBuilder。同时在循环里拼接字符串尤其要注意str x在循环里会不断创建新对象性能极差正确写法是用StringBuilder.append()。你把这个写成代码题比如“用StringBuilder实现字符串反转”就是一道德州扑克式的送分题。4. 集合框架的考点地图从ArrayList到HashMap4.1 先搭一遍整体框架集合框架是Java笔试的重头戏科陆这份试卷也花了大量篇幅在集合上。我建议你复习的时候先从整体框架入手不要一上来就抠HashMap源码。Java集合大致分为两大阵营Collection和Map。Collection下面有List、Set、QueueMap是一个独立体系。围绕这套框架最基础也是最容易扣分的题是哪些集合允许重复元素、哪些允许null、哪些是有序的。ArrayList允许重复且允许nullHashSet不允许重复但允许nullHashMap的key和value都允许null但Hashtable和ConcurrentHashMap不允许key或value为null。TreeSet和TreeMap不允许null因为它们在插入时就要排序。这些规则不需要背表格理解每种结构的底层数据结构后自然就记住了。例如HashMap允许null是因为它有专门把null映射到0号桶的逻辑ConcurrentHashMap不允许null是为了避免并发下的二义性问题。4.2 ArrayList与LinkedList不只是“数组和链表”的区别有一道经典送分题ArrayList和LinkedList的区别是什么标准答案是ArrayList底层是数组查询快、增删慢LinkedList底层是双向链表增删快、查询慢。这个答案没问题但只答到这个程度分数只能算及格。如果我是阅卷人我更希望看到下面这批补充ArrayList的初始容量是10每次扩容为原来的1.5倍oldCapacity (oldCapacity 1)扩容需要Arrays.copyOf是一次O(n)的数组复制。LinkedList的增删快是分位置的。如果从中间插入它确实要遍历到指定位置复杂度是O(n)但插入本身是O(1)。而ArrayList中间插入的代价不仅是移动元素还可能要触发扩容。LinkedList实现了Deque接口所以它可以当队列和双端队列用。这一点笔试很少直接考但面试的时候可以主动提证明你对接口体系有概念。还有一个容易忽视的点Vector。现在很多人根本没写过Vector但笔试如果考“Vector和ArrayList的区别”你至少要知道Vector是线程安全的方法用synchronized修饰性能比ArrayList差扩容是翻倍。但这类老容器在实际开发中已经很少有人用了优先用Collections.synchronizedList或CopyOnWriteArrayList。4.3 HashMap的底层原理从2017年到今天的核心考题如果说集合框架里只能复习一个点那一定是HashMap。2017年这份卷子里有关于HashMap的题我记得很清楚问的是为什么HashMap是非线程安全的。放在当时能答出JDK 1.7中并发扩容可能形成环形链表导致死循环的人很少能进一步答出JDK 1.8改为尾插法解决了链表死循环问题但仍可能丢数据的人更少。如果你的目标是现在的Java面试HashMap的追问深度至少要覆盖到以下几条这基本是当前互联网公司面试的标准链路HashMap底层数据结构是什么JDK 1.8后是“数组链表红黑树”。链表长度超过8且数组长度大于等于64时链表转红黑树。为什么链表长度为8转红黑树官方注释里给了泊松分布的概率计算负载因子0.75、长度8的情况下链表长度达到8的概率低到千万分之一。为什么数组长度必须是2的幂因为计算下标用的(n - 1) hash等价于hash % n但位运算更快同时要求n是2的幂才能让结果均匀分布。为什么负载因子默认是0.75这是空间和时间的一个折衷过大容易导致哈希碰撞增加过小则浪费空间。扩容流程是什么先判断是否需要扩容再创建新数组然后重新计算每个节点的下标位置。JDK 1.8针对树的拆分和链表的拆分有不同的逻辑。笔试不太可能让你把源码全写出来但一定会用选择题考其中一两个点。我当时准备时是把put()方法和resize()方法的JDK 1.8源码完整读了三遍然后把每个分支对应到一道题上比如“map.put(1, a)执行时如果数组下标位置为空会发生什么”。答案是直接new Node并插入。看似简单但这就是源码阅读能力的体现。4.4 Comparable与Comparator排序题的隐藏考点热词里有java comparator.comparing 将某元素值放第一个这其实对应了Java排序的一个高频考法。笔试题经常这样出有一个Student类有name和age字段让你先按年龄升序如果年龄相同按姓名降序写出排序代码。使用Comparable的话让Student实现compareTo方法使用Comparator的话用匿名内部类或Lambda表达式。注意Lambda写法的优先级Comparator.comparing(Student::getAge).thenComparing(Student::getName, Comparator.reverseOrder())。或者用Comparator.comparing(Student::getAge).thenComparing(Student::getName, (a, b) - b.compareTo(a))。如果你要用Comparator.comparing把某个元素排到第一位比如把年龄为0的元素排到最前面可以这样写ComparatorStudent comparator Comparator.comparingInt(s - s.getAge() 0 ? 0 : 1);这个写法的核心思路是通过映射函数把元素分成“特殊组”和“普通组”特殊组排在前面。很多人在面试时喜欢炫这种写法但在笔试里我建议你老老实实写清楚Comparator的实现逻辑避免阅卷人一眼看不出你的意图。5. 多线程与并发区分初级与中级的分水岭5.1 synchronized与Lock从“用过”到“实现原理”科陆2017年的试卷里多线程题主要停留在synchronized关键字的使用层面。比如问synchronized加在静态方法和实例方法上的区别前者锁的是Class对象后者锁的是当前实例对象。这个知识点即便到了今天依然是基础中的基础。但要准备现在的面试你需要深入一层。synchronized在JDK 1.6之后经过了大量优化锁有四种状态无锁、偏向锁、轻量级锁、重量级锁。锁会根据竞争情况从偏向锁逐渐升级到重量级锁但一般不会降级。面试官如果问你“synchronized和Lock的区别”你可以从这几个角度展开synchronized是关键字自动释放锁Lock是接口必须手动unlock通常放在finally里。synchronized在JDK 1.6后性能已经和Lock相差无几但Lock支持可中断、可超时、可公平锁功能更丰富。synchronized不可中断Lock.lockInterruptibly()可以响应急中断。ReentrantLock默认非公平锁可以通过构造器参数改为公平锁。但公平锁性能较差因为线程切换更频繁。笔试不会让你写一个完整的并发程序但可能会让你判断一段代码是否存在线程安全问题。如果你能看到自增操作count那道题必须答出这一步不是原子操作它分为读取、加1、写回三步在并发环境下会丢失更新。正确的做法是使用AtomicInteger或者ReentrantLock或者简单粗暴加synchronized。5.2 volatile可见性问题的一道必答题volatile几乎是多线程题里每卷必有的考点。最经典的例子是双端检锁的单例模式public class Singleton { private static volatile Singleton instance; public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }为什么instance要加volatile因为new Singleton()在字节码层面不是一个原子操作大致分为三步分配内存、执行构造方法初始化、将引用指向内存地址。在没有volatile的情况下JVM可能进行指令重排序导致第二个线程拿到一个“已经分配内存但还没执行构造方法”的对象从而在使用时出错。volatile禁用了重排序保证了“先初始化再赋值”的语义。从笔试答题角度volatile还有两个特性要背熟保证可见性、不保证原子性。volatile让变量每次读取都从主内存中读每次修改都立即刷新到主内存从而解决了多线程下的可见性问题。但它解决不了复合操作的原子性问题比如count。凡是答“volatile能保证线程安全”的都会被扣分。5.3 线程池面试官最喜欢“顺手一问”的点科陆当年的试卷里有没有线程池的题我不太记得清了但即便没有这个点也是Java工程师面试绕不开的。笔试如果考通常就是问线程池的几个核心参数核心线程数、最大线程数、空闲存活时间、工作队列、线程工厂、拒绝策略。我推荐你记一个标准答案模板方便笔试简答题直接套用线程池的核心线程数corePoolSize表示即使线程空闲也会保留的线程数量maximumPoolSize表示线程池允许的最大线程数keepAliveTime表示非核心线程空闲多久后被回收workQueue用于存放等待执行的任务RejectedExecutionHandler定义任务太多无法执行时的处理策略常见的四种策略是AbortPolicy抛异常、CallerRunsPolicy调用者线程执行、DiscardPolicy丢弃、DiscardOldestPolicy丢弃最旧任务。如果你看到“线程池为什么不能直接使用Executors.newFixedThreadPool()”这类题原因是Executors创建的固定线程池使用的是无界LinkedBlockingQueue高峰期任务可能越积越多最终导致OOMExecutors.newCachedThreadPool()可创建的线程数上限是Integer.MAX_VALUE也可能OOM。所以生产环境推荐手动通过ThreadPoolExecutor构造函数创建线程池并指定有界队列和拒绝策略。6. JVM内存与调优OOM类题目的正确打开方式6.1 运行时数据区域的划分JVM相关题目在科陆这套2017年的试卷里占的篇幅不算多但必考。热词里有个java: outofmemoryerror: insufficient memory这其实是很多人跑项目时不理解堆内存日志导致的问题。笔试的经典考法是让你写出JVM运行时数据区有哪些部分以及每部分的作用。标准答案是程序计数器、虚拟机栈、本地方法栈、堆、方法区。其中程序计数器是私有区域存放当前线程正在执行的字节码指令地址虚拟机栈存放栈帧每个方法调用对应一个栈帧入栈和出栈堆是最大的内存区域存放对象实例方法区存放类的元数据、静态变量、常量池JDK 1.8后方法区由元空间实现不再用永久代。这里有一个经常被问到的细节StackOverflowError什么时候抛当递归调用的层数太深导致虚拟机栈无法分配新的栈帧时抛出。OutOfMemoryError什么时候抛当堆内存无法为新对象分配空间时抛出常见于大对象过多或内存泄漏。6.2 垃圾回收从标记算法到经典收集器垃圾回收算法是笔试的另一个高频点。你需要答出三个基本算法标记-清除、复制、标记-整理。然后说清楚它们在新生代和老年代分别怎么用。新生代采用复制算法因为新生代对象大部分“朝生夕灭”存活率低。Eden区和两个Survivor区的比例默认是8:1:1每次Minor GC后存活对象复制到Survivor区到了一定的年龄阈值默认15就晋升到老年代。老年代对象存活率高不适合复制算法因此采用标记-清除或标记-整理。笔试如果能答到CMS和G1的区别基本就是加分项了。G1引入了Region的概念把堆划分成多个大小相等的Region可以预测停顿时间JDK 9之后是默认垃圾回收器。2017年的试卷大概率还在问Serial、Parallel和CMS但今天你最好把G1和ZGC也了解一下。6.3 面对OOM报错应该怎么排查面试题经常给一个场景系统运行一段时间后抛出OutOfMemoryError: Java heap space问你如何排查。这题属于典型的过程题只看你有没有真正的排障经验。第一步启动时加上-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/path/to/dump.hprof这样OOM发生时自动导出堆快照。第二步用jmap -dump:formatb,fileheap.hprof pid手动导出当前堆快照。第三步用MAT或VisualVM分析工具打开hprof文件看内存中被哪些对象占满特别关注BigDecimal、byte[]、char[]这类大对象。第四步在代码层面排查是否存在未关闭的连接、静态集合不断增长、ThreadLocal没有清理等问题。笔试没有条件让你实操但你可以把步骤写出来然后给一个判断标准如果内存快照里同类型对象有海量实例基本可以判定为内存泄漏如果没有只是整体内存占用过高那就是内存不足需要调大堆内存或优化对象结构。这条把“内存泄漏”和“内存溢出”区分开的回答能在阅卷时给你提分。7. 编程题算法不是难点规范才是7.1 冒泡排序不丢分也不是靠硬背科陆这套试卷的编程题结合热词来看冒泡排序和快速排序是重点。编程题在笔试阅卷时一般三步走对不对、优不优、规不规范。所以哪怕你会写也要注意代码风格。冒泡排序的典型写法public static void bubbleSort(int[] arr) { for (int i 0; i arr.length - 1; i) { boolean swapped false; for (int j 0; j arr.length - 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)。稳定排序因为相邻元素交换不会改变相等元素的相对顺序。笔试只要问“冒泡排序是否稳定”你就答“稳定”理由是只有才交换遇到等于不交换。7.2 快速排序边界条件决定成败快速排序在笔试手写题里的地位比冒泡更高因为面试官能从你写的边界条件看出你对递归和指针的理解。我建议你背熟下面这个版本public static void quickSort(int[] arr, int left, int right) { if (left right) { return; } int pivot partition(arr, left, right); quickSort(arr, left, pivot - 1); quickSort(arr, pivot 1, right); } private static 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; }这个版本采用Lomuto分区方案逻辑简单笔试中不容易写错。关键点在递归的终止条件left right。很多人在笔试时忘了写这个导致无限递归栈溢出。另一个容易出错的地方是quickSort(arr, left, pivot - 1)这里必须是pivot - 1因为pivot已经落在正确的位置上了再排它会死循环。快速排序的时间复杂度平均是O(n log n)最坏情况下几乎有序时选到最值做pivot退化到O(n²)。空间复杂度是递归栈的深度平均O(log n)最坏O(n)。不稳定排序。笔试把这些写清楚编程题分数基本能拿全。7.3 编程题的答题规范别让阅卷人猜你的代码编程题哪怕写对了代码不规范也很吃亏。我总结了几条校招笔试的码风建议变量命名要有意义。不要写int a 0要写int count 0。括号对齐。考试系统没有IDE的自动格式化自己写的时候每行缩进固定为4个空格。把关键步骤写注释。比如冒泡排序里写一句// 本次循环是否发生交换若未发生则数组已有序能让阅卷人一眼看出你的思路。优先用标准库。如果题目没限制不能使用API排序直接用Arrays.sort()但前提是你要在注释里说明Arrays.sort()底层使用的是DualPivotQuicksort或TimSort复杂度是多少这比强行手写一个不稳定的算法更能拿分。不要留空。即使不会也要写下暴力法或解题思路写“先遍历再用HashMap记录出现次数”也算分。8. 数据库与框架初级Java工程师的另一半技能8.1 SQL基础与索引的几种考法科陆这套试卷还涉及不少数据库题目这是Java后端笔试的标配。虽然试卷没有明确给出SQL语句但高频考点无非是三块SELECT查询语法、多表连接、索引。多表连接里有三个最容易混淆的点INNER JOIN、LEFT JOIN、RIGHT JOIN。笔试最常见的形式是给你两张表的数据问LEFT JOIN之后的结果有多少行。你只要记住LEFT JOIN会保留左表的所有行右表没有匹配的记录就用NULL填充。这个规则听起来简单但我当年考试时因为没注意“左表有一行在右表没有匹配”结果少算了一行。索引相关的题我会从概念题和场景题两个方向准备。概念题问索引的数据结构答案是B树并说明为什么用B树而不是B树B树所有数据都存在叶子节点且叶子节点之间有链表指针范围查询只需遍历链表效率远高于B树。场景题给你一条SQL比如SELECT * FROM user WHERE age 20 AND name 张三问你如何建索引。正确答案是(name, age)联合索引原则是“等值查询的列放前面范围查询的列放后面”。8.2 Spring与MyBatis框架题不能只写“用过”2017年的Java笔试卷框架部分大概率还是围绕SSM来出题也就是Spring、Spring MVC和MyBatis。虽然现在很多公司已经转向Spring Boot但Spring的核心思想没有变。如果笔试问Spring两大特性记得回答IoC控制反转和AOP面向切面编程。IoC是容器帮助我们管理对象的创建和依赖关系AOP是把日志、事务、权限等横切逻辑从业务代码中剥离出来。后面可以紧跟一个例子声明式事务就是AOP在Spring中的典型应用当方法上标注Transactional时Spring会通过代理在方法执行前开启事务执行后提交或回滚。MyBatis部分的经典题目是#{}和${}的区别。#{}是预编译占位符会生成PreparedStatement的?可以有效防止SQL注入${}是字符串拼接直接替换SQL语句存在注入风险。笔试如果问你“排序字段要动态传入应该用#{}还是${}”答案是${}因为排序字段不能作为参数值预编译但必须做白名单校验防止传入非法字段名。这个细节能体现你真实写过MyBatis动态SQL。9. 我当时是怎么复习这类试卷的一份实操复盘9.1 三轮复习法不要一上来就刷真题针对科陆这套试卷以及同类校招Java笔试题我复习的思路比较简单粗暴一共分三轮。第一轮是系统过知识点用的资料是《Java编程思想》里集合、异常、并发相关的章节加上一份网上整理的很完整的Java面试题汇总。这一轮的目的是把所有可能考到的考点过一遍不懂的地方先记录不深究。第二轮开始做真题和模拟题把错的题整理成错题集重点关注反复出错的类型比如字符串比较、集合扩容。第三轮是模拟实战给自己限时90分钟按照真实笔试的感觉做一整套卷子然后集中复盘。这个三轮复习法的核心逻辑是“先建立知识体系再通过刷题找漏洞最后通过模拟训练考试节奏”。很多人备考时会犯一个错误一上来就狂刷题刷到哪算哪结果知识点之间是断裂的考试时碰到一道融合了集合和并发的综合题就不知道从哪下手了。9.2 笔试时间分配90分钟的试卷编程题要留45分钟科陆这种试卷一般题量在30道左右选择题和简答题占一半编程题两三道。我的建议是选择题控制在20分钟内做完不会的立刻标记跳过不要在一个语法细节上纠结。简答题控制在25分钟内每题写三到五行关键点写全即可。编程题至少留45分钟因为编程题不仅要写代码还要留时间检查边界条件。选择题如果遇到完全不会的题我的策略是“先排除最不可能的选项”。比如题干问“哪个集合是线程安全的”先看有没有CopyOnWriteArrayList如果有大概率是它如果没有再在Vector和Hashtable里选。这种排除法虽然不能保证全对但能显著提高蒙对的概率。9.3 给备考者的一点真实建议写了这么多最后说几句实在的。一份2017年的试卷放到今天看考察的知识点覆盖率依然很高因为Java工程师的核心知识栈不像前端框架那样日新月异。如果你正在准备校招不必被网上铺天盖地的新框架名词吓到先把集合、多线程、JVM、SQL这四个大件吃透再拿几套历年真题练手效果比盲目追热点好得多。我当时把这份卷子里每一道错题都做了拓展比如错了HashMap非线程安全的题就顺手把ConcurrentHashMap的JDK 1.7分段锁和JDK 1.8 CASSynchronized的实现区别也整理了错了String创建的题就顺手把String常量池、intern()方法、字符串变量拼接的字节码全部过了一遍。这套“由一道题带出一个知识块”的复盘方法我受用至今。面试官最看重的不是你背了多少答案而是你能不能举一反三——这一点无论在2017年还是现在都从未变过。