
简介金航数码Java初级笔试题是一份面向初级Java开发岗位笔试与基础语法复习的精选题目合集。文档以20道单项选择题系统覆盖循环控制、字符串拼接、标识符命名、抽象方法声明、继承规则、数组与Object类型转换、异常处理流程等核心考点并从do-while边界条件、字符串自动转型、编译错误位置、子类构造器执行顺序、static变量与局部变量的区别、finally块与catch块执行时机等角度逐一拆解易错点给出选项排除思路与知识延伸。资源共1个Word文档doc压缩包仅98KB全部内容集中在一个文档内适合离线查看、打印自测或作为备考笔记保存。已有392人浏览学习对正准备技术笔试或想快速夯实Java基础概念的初学者是一份轻量且直接可用的练习材料尤其适合在笔试前集中刷题、对照解析复盘快速扫清语法盲区。1. 金航数码的 Java 笔试题通常是一张 6090 分钟的混合卷金航数码的 Java 笔试题通常是一张 6090 分钟的混合卷基本盘很固定Java 基础与集合、线程与 JVM、Spring 事务和 MySQL SQL、最后一道手写算法。真正卡人的不是题目难而是八股背得熟、换个问法就答不到点上——比如问 HashMap 什么时候树化一半人张口就是 8 和 64却说不出链表长度 8 指什么、为什么要到 64 才真正转红黑树。下面按答题思路拆一遍这套卷子先讲机制再给能直接照抄的代码和参数表。适合把金航数码 java 笔试题当目标、想靠理解而非死记的 Java 工程师也适合顺手准备其他企业级面试。2. Java 基础与集合框架String、equals/hashCode 与 HashMap 源码笔试卷子里选择题和填空题占分最多的就是 Java 基础与集合。企业级项目里天天用集合但真正写对 equals、说清扩容机制的人不多这章把三类必考题拆开讲。2.1 String 的 与 equals一道题串起常量池和不可变性常见问法String a new String(abc) 创建了几个对象 标准答法是如果常量池里还没有 abc会先在堆里 new 一个 String 对象同时把字面量放入常量池所以是创建了两个对象如果常量池里已有 abc则只创建一个新的堆对象。这里有个容易踩的细节JDK 7 之后字符串常量池已经移到了堆里但字面量的复用机制没有变判断题里说常量池在方法区是错的。紧接着的追问通常是为什么 String 要设计成 final 和不可变三个理由要能说全其一常量池缓存和 String 对象复用依赖内容不可修改否则所有缓存引用全部失效其二String 大量用作 HashMap 的 key允许内容变化会直接破坏哈希表结构定位不到原条目其三不可变对象天然线程安全不需要加锁。答到这里再把 StringBuilder可变、非线程安全和 StringBuffer可变、线程安全的对照补一句这道题就算圆满了。2.2 equals/hashCode 的契约以及 HashMap 的 put 流程重写 equals 为什么必须重写 hashCode是保留题契约只有一句话equals 相等的两个对象hashCode 必须相等hashCode 相等的两个对象equals 不一定相等。只重写 equals 不重写 hashCode两个内容相同的对象放进 HashSet 会被当成两个元素因为哈希结构查找时先比哈希值再比 equals。下面这段是笔试题的标准答案模板public class User { private String name; private int age; Override public boolean equals(Object o) { if (this o) return true; if (!(o instanceof User)) return false; User user (User) o; return age user.age Objects.equals(name, user.name); } Override public int hashCode() { return Objects.hash(name, age); } }逻辑说明equals 先做引用相等判断再用 instanceof 过滤类型不匹配的对象最后比较参与业务含义的字段hashCode 把 equals 里用到的字段全部纳入计算保证等则同哈希。字段比较用 Objects.equals 而不是 name.equals是为了让 name 为 null 时不抛空指针。HashMap 的 put 流程是必背但容易背串的内容。JDK 8 里 put 时先对 key 的 hashCode 做扰动(h key.hashCode()) ^ (h 16)这里用到的^异或和无符号右移就是一道隐含的 java 运算符考点——让高 16 位参与低位散列减少碰撞再用(n - 1) hash定位桶下标n 必须是 2 的幂才能保证这个位运算等价于取模。桶为空直接放新节点桶非空则沿链表或红黑树比较 key相等就覆盖值链表长度超过 8 且数组长度达到 64 时树化为红黑树。扩容条件是size thresholdthreshold 等于容量乘加载因子 0.75扩容直接翻倍并把旧元素重新散列。参数表直接背这张参数默认值笔试题常考含义initialCapacity16必须是 2 的幂配合(n-1) hash取模loadFactor0.75空间与时间折中过大冲突多过小浪费空间threshold12容量 × 加载因子达到即触发扩容TREEIFY_THRESHOLD8链表长度达到 8 时尝试树化MIN_TREEIFY_CAPACITY64数组长度达到 64 才真正树化否则先扩容2.3 ArrayList 与 LinkedList别把增删快慢答反ArrayList 和 LinkedList 有什么区别出现频率极高陷阱在于很多人脱口而出LinkedList 增删快。实际要看操作位置ArrayList 在尾部 add 是摊还 O(1)按索引 get 是 O(1)LinkedList 只有拿到节点引用时头尾操作才是 O(1)按索引 get 要遍历到位置是 O(n)。删除同理ArrayList 删中间元素要搬移后半段LinkedList 要先遍历定位。java.util.ArrayList初始容量是 10之后按 1.5 倍扩容复制走Arrays.copyOf。把这些 java 容器类的关键细节记牢后另一个常考判断题也能顺带答准单线程里用 foreach 删除元素会抛 ConcurrentModificationException因为迭代器检查到 modCount 变化用迭代器自己的 remove 方法不会抛。延伸一句多线程共享集合优先考虑 CopyOnWriteArrayList 或 ConcurrentHashMap给阅卷人留个工程视角的印象。3. 并发与 JVM 考点线程池参数、volatile 与 GC 选择题并发题在企业级笔试卷里几乎不缺席后端服务绕不开线程池。这一章的题通常长这样给一段带 volatile 的代码问输出结果或者给 ThreadPoolExecutor 的参数问任务执行顺序。3.1 volatile 与 synchronized三条语义各管什么先记结论synchronized 保证原子性、可见性、有序性volatile 只保证可见性和有序性不保证原子性。经典考点是count两个线程同时对 volatile 变量自增各一万次最终结果必然小于 20000因为自增是读-改-写三步volatile 管不住指令中间被打断。volatile 的第二层语义是禁止指令重排典型应用就是第五章要写到的单例双检锁。笔试简答里可以再提一句volatile 写操作在 x86 上会触发内存屏障把写缓冲刷到主存这是它保证可见性的底层机制。能答到内存屏障这一层基本能区分开背答案和真理解。3.2 ThreadPoolExecutor 七参数与任务执行顺序线程池的执行顺序是高频题正确顺序是核心线程满 → 新任务进工作队列 → 队列满再开非核心线程 → 达到最大线程数后走拒绝策略。很多人答成先开线程到最大再排队直接错。代码题的标准写法如下ThreadPoolExecutor pool new ThreadPoolExecutor( 2, // corePoolSize常驻线程数 4, // maximumPoolSize含临时线程的最大线程数 30L, TimeUnit.SECONDS, // keepAliveTime临时线程空闲存活时间 new ArrayBlockingQueue(100), // workQueue有界阻塞队列 Executors.defaultThreadFactory(), // threadFactory可自定义线程名和 daemon 标志 new ThreadPoolExecutor.CallerRunsPolicy() // handler触发拒绝时被调用 );参数说明corePoolSize 和 maximumPoolSize 的差值就是忙时临时扩出来的线程keepAliveTime 到期后回收队列选有界 ArrayBlockingQueue 比无界队列稳妥避免任务无限积压打爆内存这也是不建议直接用 Executors.newFixedThreadPool 的原因——它默认挂的是无界 LinkedBlockingQueue。CallerRunsPolicy 表示拒绝时由提交任务的线程自己执行适合不想丢任务的场景。四种拒绝策略要背全这张表直接抄策略行为适用场景AbortPolicy抛 RejectedExecutionException默认策略用于必须感知过载CallerRunsPolicy提交任务的线程自己执行不能丢任务允许消费降速DiscardPolicy静默丢弃新提交的任务允许丢任务且监控完备DiscardOldestPolicy丢弃队列最老任务后重试优先保最新任务可接受旧任务作废简答题另一个高频点是如何等待所有线程都完成。线程池场景下用 shutdown 配合 awaitTermination或者直接用 CountDownLatchCountDownLatch latch new CountDownLatch(3); for (int i 0; i 3; i) { new Thread(() - { try { // 业务逻辑 } finally { latch.countDown(); } }).start(); } latch.await(10, TimeUnit.SECONDS);逻辑说明主线程调用 await 阻塞直到计数归零或超时返回。countDown 必须放在 finally 里否则业务抛异常会导致计数永远到不了零主线程一直卡死。可以顺带补一句 CyclicBarrier 的区别它是可复用的汇合点等 N 个线程到齐后一起放行CountDownLatch 是一次性门闩这是两个组件最本质的差别。3.3 JVM 内存分区与对象分配简答题JVM 题问对象存在哪儿标准答案是绝大多数对象在堆上分配但经过逃逸分析确认不逃逸出方法后可以栈上分配并做标量替换减轻 GC 压力局部变量存虚拟机栈的局部变量表对象引用在栈、实例数据在堆类元信息在方法区JDK 8 之后叫元空间 Metaspace。笔试题填内存分区表时记住五块程序计数器、虚拟机栈、本地方法栈、堆、方法区。其中程序计数器是唯一不会 OOM 的区域栈溢出抛 StackOverflowError堆和方法区都可能抛 OOM。堆内部再分新生代和老年代新生代又分为 Eden 和两块 Survivor。3.4 GC 简答题从是什么答到怎么做问如何判断对象可回收要答两层引用计数法有循环引用缺陷所以主流虚拟机用可达性分析从 GC Roots 出发不可达的对象可回收。GC Roots 包括虚拟机栈中引用的对象、静态属性引用的对象、常量引用的对象、JNI 引用的对象。再往下会被追问什么对象会进老年代三个考点大对象直接进老年代可以用 -XX:PretenureSizeThreshold 指定阈值长期存活的对象超过 MaxTenuringThreshold 年龄阈值进入动态年龄判定满足条件也会提前晋升。如果卷子里出现调优选择题给参数比讲概念有用java -Xms2g -Xmx2g -Xmn512m -XX:SurvivorRatio8 -XX:UseG1GC -jar app.jar说明Xms 和 Xmx 设成相同值避免运行期频繁扩容抖动Xmn 指定新生代大小SurvivorRatio8 让 Eden 与单个 Survivor 的比例为 8:1G1 是 JDK 9 之后的默认收集器目标是把 STW 停顿控制在一个范围内。答题时把参数含义写全比只写命令得分高。4. Spring 事务与 MySQL 隔离级别数据库题的踩分写法行业信息化项目的核心是业务系统Spring 和数据库的题目在这类卷子里占比最高。很多人死在同一个地方事务概念背得熟但不知道哪些写法会让事务失效SQL 能写出来但不带边界条件。4.1 Spring Bean 生命周期与循环依赖的三级缓存问 Bean 生命周期按阶段答实例化走构造器→ 属性填充 → Aware 接口回调 → BeanPostProcessor 的 postProcessBeforeInitialization → InitializingBean 的 afterPropertiesSet 和 init-method → BeanPostProcessor 的 postProcessAfterInitialization → 使用 → DisposableBean 的 destroy。时间不够就抓两个关键点属性填充发生在实例化之后BeanPostProcessor 是 Spring 生成 AOP 代理的切入时机。循环依赖的题通常问Spring 怎么处理单例 Bean 的循环依赖。答案指向三级缓存一级缓存存完整 Bean二级缓存存早期暴露的原始对象三级缓存存 ObjectFactory 工厂。之所以要第三级而不是提前在二级缓存放代理对象是因为代理可能要到属性填充之后才生成过早创建会导致代理对象错位。注意构造器注入无法解决循环依赖因为构造阶段对象还没暴露给容器。顺着 AOP 代理往下面试官大概率追问 java 动态代理接口用 JDK 动态代理基于 InvocationHandler类用 CGLIB基于子类继承二者的选择机制和性能差异是常见延伸题。4.2 Transactional 的传播行为与五个失效场景传播行为有七种笔试最常考三种REQUIRED 是默认值有事务就加入、没有就新建REQUIRES_NEW 挂起当前事务新建一个常用于独立的日志记录NESTED 是嵌套事务Spring 里基于 savepoint 实现内层回滚不影响外层已提交的数据。事务失效场景是加分项五个至少能背出三个方法不是 public同类内部自调用 this.method() 绕过代理异常被 catch 吞掉没有抛出去抛出 checked 异常默认不回滚表引擎不支持事务比如 MyISAM。直接给一段会失效的代码再指出问题public class OrderService { public void outer() { inner(); // 自调用Transactional 不生效 } Transactional public void inner() { // 更新订单状态 } }逻辑说明outer 通过 this 调用 inner事务增强靠的是代理对象this 指向原始对象代理拦截不到这次调用注解因此失效。解决办法是注入自身代理对象或者拆到另一个 Service。答题时把这段代码默写出来阅卷人一眼就能看出你踩过这个坑。注意五个失效场景里异常被 catch 吞掉是生产事故出现频率最高的一个。定位事务不回滚时先找方法里有没有 catch 块再去查注解配置顺序不能反。4.3 MySQL 隔离级别、脏读与当前读MySQL 默认隔离级别是 REPEATABLE READ这一点和 Oracle 的 READ COMMITTED 不同是判断题最爱挖的坑。四种隔离级别的对比表背熟隔离级别脏读不可重复读幻读READ UNCOMMITTED可能可能可能READ COMMITTED不会可能可能REPEATABLE READ不会不会可能InnoDB 下多数场景已避免SERIALIZABLE不会不会不会InnoDB 在 REPEATABLE READ 下用间隙锁和 MVCC 解决了大部分幻读所以追问点转向快照读和当前读的区别普通 SELECT 是快照读走 MVCC 版本链不加锁SELECT ... FOR UPDATE、UPDATE、DELETE 是当前读必须读最新已提交版本并且加锁。间隙锁锁的是记录之间的区间所以当前读能挡住并发插入这句答出来隔离级别这题的深度就够了。4.4 一道必练的典型 SQL每科成绩最高分企业笔试题里的 SQL 通常落在学生成绩表查每科前三这类分组取极值上一张表既考 GROUP BY 又考窗口函数。两种写法都要准备-- 写法一每科最高分但拿不到该学生信息 SELECT course_id, MAX(score) AS max_score FROM student_score GROUP BY course_id; -- 写法二每科前三名MySQL 8.0 窗口函数 SELECT course_id, student_id, score FROM ( SELECT course_id, student_id, score, ROW_NUMBER() OVER (PARTITION BY course_id ORDER BY score DESC) AS rn FROM student_score ) t WHERE rn 3;逻辑说明写法一的 MAX 只返回分数值无法关联到学生写法二用 PARTITION BY 按课程分区ORDER BY score DESC 组内排序ROW_NUMBER 生成组内序号外层再过滤前三名。需要并列名次时把 ROW_NUMBER 换成 DENSE_RANK。阅卷时最反感的是聚合查询不带 ORDER BY写完后自己扫一眼有没有补上。工程上如果字段建了 (course_id, score) 联合索引窗口函数的排序可以走索引扫描不用额外 filesort这一点写在答案末尾是个加分动作。5. 手写代码题的边界条件与答案组织技巧金航数码的笔试卷最后通常是 12 道手写题常见题目是单例、冒泡排序、字符串反转。难度都不高但阅卷人重点看边界处理和思路清晰度最后一章给一套直接能用的答题模板。5.1 DCL 单例为什么必须 volatile双检锁不加 volatile会出现拿到半初始化对象的问题instance 的引用赋值可能被重排到构造完成之前另一个线程读到非 null 引用后直接使用对象内部字段还是默认值。volatile 禁止引用赋值与构造的重排。手写模板如下public class Singleton { 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; } }写完主动解释 instance 加 volatile 的原因这个细节就是拉开差距的踩分点。只写代码不解释这道题只能拿一半分。5.2 冒泡排序的工程化写法排序题不要写教科书最朴素的版本要体现边界控制public static void bubbleSort(int[] arr) { if (arr null || arr.length 2) return; 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 tmp arr[j]; arr[j] arr[j 1]; arr[j 1] tmp; swapped true; } } if (!swapped) break; } }说明两点内层循环用arr.length - 1 - i控制边界数组越界异常在笔试题里最常见就发生在这一行少了- 1或- iswapped 标记让已有序数组提前退出最好情况退化到 O(n)。空数组和单元素数组的防护写在方法第一行阅卷人会默认你有防御性编程意识。字符串反转的边界在toCharArray()后双指针交换不要用 charAt 逐个拼字符串那会造成 O(n²) 的字符串复制开销。快排的递归退出条件必须是 left right这是写递归题最容易漏的一个分支。5.3 做题顺序与卷面组织拿到卷子先花两分钟扫一遍所有题目优先做有把握的拿不准的标记出来最后回头。时间分配上一般按选择题 30%、简答 30%、SQL 20%、手写 20% 控制手写题先写一行解题思路再写代码最后注释标明时间复杂度和空间复杂度。这样即使代码有个别小错阅卷人也能根据思路给分。最亏的是编译不通过级别的错误——写完在心里把边界用例跑一遍再落笔构造器默认值和数组下标这两处是错得最集中的位置检查完再交卷。本文还有配套的精品资源点击获取