首页 / 资讯中心 / 文章详情

Java初级笔试题深度解析:循环、继承、SQL与单例的避坑指南

Java初级笔试题深度解析:循环、继承、SQL与单例的避坑指南 ★ FEATURED ARTICLE
简介这份金航数码Java笔试题面向初级Java开发者与求职者精选了覆盖基础语法核心考点的典型选择题重点考察循环控制、字符串连接、变量命名规则、抽象类与继承关系、数组与对象转换以及异常处理机制。资源为doc格式文档压缩包内共1个文件体积约98KB便于直接阅读或打印练习目前已有395人学习浏览。文档不仅给出正确答案还针对易错点展开解析例如第1题通过do-while循环与后置自减边界条件得出结果为-1第2题说明整数与字符串拼接的隐式转换第3题强调标识符不能以#、~等特殊字符开头第7题区分数组与Object类型转换的编译时限制第10、11题梳理try-catch-finally及异常匹配的完整输出流程。除了解析文档以单项选择题形式呈现可直接作为模拟试卷限时自测帮助熟悉企业笔试题型。这些题目覆盖面试高频基础点既能帮助初学者自检语法掌握程度也能让求职者在笔试前快速查漏补缺巩固面向对象与异常处理等关键概念为技术面试做好充分准备。1. 一套Java初级笔试题先搞清它考什么再决定值不值得刷朋友发来一套某航数码的Java初级笔试题20道单选加4道上机。我第一反应是初级题能难到哪去结果逐题对完答案才发现这套题几乎把Java新人最容易翻车的语法点全挑出来了do-while后置自增自减、字符串拼接顺序、变量命名合法性、抽象方法大括号、继承初始化顺序、异常与finally、synchronized互斥锁外加单例模式和Oracle部门员工表的SQL。适合两类人一类是投Java初级岗前想自测基础的人另一类是负责带新人、需要一份入职摸底卷的老手。刷这套题的价值不在题目本身而在那些一看就会、一写就错的细节上。2. 选择题逐题拆循环边界、字符串拼接与继承初始化里的送命题2.1 循环与作用域不是算出结果而是先看能不能编译第1题是整套题里最容易算错的一道int i 10; do { i i / 2; } while (i-- 1); System.out.println(i);很多人的推导是10除以2得55大于1成立i自减成44除以2得22大于1成立i自减成11除以2得0循环结束输出0。这个推导错在把i-- 1理解成先自减再比较。实际上后置自减的语义是先拿 i 的当前值参与比较比较完再让 i 减1。正确推演是第1轮i10进入循环体后 i5判断 51 成立然后 i 变成4。第2轮i4进入循环体后 i2判断 21 成立然后 i 变成1。第3轮i1进入循环体后 i0判断 01 不成立然后 i 变成-1。所以输出-1。这类题目唯一的稳妥做法是拆开把while (i-- 1)改成while (oldValue(i) 1)或者在心里先写一行boolean flag i 1; i i - 1;再决定循环是否继续。千万别在脑里口算后置自减配合除法的循环十个人九个会算错。第6题则换了个考法问for (int i0; i10; i) { if (i6) break; }之后System.out.println(i)输出什么。正确结论是 Compilation fails因为 i 声明在 for 的初始化语句里作用域只在循环块内循环结束 i 就不可见了。这道题真正的考点不是break而是变量作用域。很多候选人只盯着i到7就break忽略了编译都过不了。把 i 改成在 for 之前声明结果才是7。第7题是关于数组和Object转型的Object obj new int[] { 1, 2, 3 }; int[] someArray (int[])obj; for (int i : someArray) { System.out.print(i ); }这段代码完全合法int数组是Object的子类型引用赋值给Object没问题向下转型也是允许的foreach遍历int数组也没问题输出1 2 3。这套题的第7题选项里有两个C明显是排版笔误但题目本身不复杂。遇到数组转型题记住向上转型自动、向下转型要强转、元素访问不受影响就够了。2.2 字符串、命名与抽象方法语法题的三个经典误导第2题问String s Hello 9 1;的结果答案是Hello91不是Hello10。原理是Java字符串拼接从左到右执行一旦左边出现String后面的加号就变成字符串连接符9先被转成9再拼上1。如果把题目改成9 1 Hello结果就是10Hello因为前两个加号先做整数加法。这类题没技巧记住字符串拼接从左到右遇到String就变拼接。第3题考标识符规则String #name Jane Doe; int $age 24; Double _height 123.5; double ~temp 37.5;Java标识符只能以字母、下划线、美元符开头#和~都不能开头所以有2处编译错误。容易误判的是$age和_height它们虽然看着别扭但完全合法。这道题提醒一件事平时写代码别用这种风格但考试时要认得它们是合法的。第4题问abstract的叙述正确答案是声明抽象方法不可写出大括号。抽象方法没有方法体声明结束就是分号写了{}反而变成普通方法。另外要注意abstract不能修饰字段修饰字段的是final或volatile那一类。每当看到A说abstract可修饰字段直接排除。2.3 继承与初始化顺序父子构造器和static的执行次序第8题输出BaseBaseclass Base { Base() { System.out.print(Base); } } public class Alpha extends Base { public static void main(String[] args) { new Alpha(); new Base(); } }new Alpha()时子类构造器第一行隐式调用super()先打印一次Base然后再new Base()又打印一次。注意是连续的BaseBase中间没有换行。第12题是整套题里最考验初始化顺序的class Super { Super(int i) { System.out.print(Super i ,); } } public class Sub extends Super { static Super s1 new Super(1); Super s2 new Super(3); Sub(int i) { super(i); System.out.print(Sub i ,); } public static void main(String[] args) { Super c1 new Sub(2); } }答案是Super1,Super3,Super2,Sub2,。执行顺序很容易搞混拆开看就清晰了类加载时先初始化静态成员s1 new Super(1)打印 Super1。main里执行new Sub(2)创建对象前先初始化实例成员s2 new Super(3)打印 Super3。然后进入Sub构造器首行super(i)即Super(2)打印 Super2。最后执行Sub构造器主体打印 Sub2。这类题我在带人的时候反复强调一个口诀类加载先静态new对象先成员再走父类构造最后子类构造体。静态成员只初始化一次实例成员每次new都会执行父类构造器永远在子类构造体之前跑完。第13题是一个隐藏的编译错误abstract class MineBase { abstract void amethod(); static int i 1; } public class Sub extends MineBase { // 没有实现 amethod() }Sub继承了一个抽象类却没有实现amethod()所以Sub本身必须是抽象类。题目里Sub不是abstract直接编译失败。至于数组int[5]默认输出00000那是在能编译的前提下才讨论的事。第9题的值传递也很经典modify(a)里的a是形参方法内a改的是副本静态变量a保持0。2.4 异常、线程与设计原则catch的顺序和synchronized的用途第10题的异常流程值得完整推一遍try { method1(); // 内部发生 10/0 System.out.print(A); } catch (Exception ex) { System.out.print(B); } finally { System.out.print(C); } System.out.print(D);method1里除零抛出ArithmeticExceptiontry剩余语句不执行进入catch打印Bfinally无论是否异常都执行打印Cfinally执行完后try-catch语句块之后的代码继续执行打印D。最终输出BCD。这道题常见错误是漏掉D以为程序在catch或finally后就结束了实际只要没有System.exit就会继续往下走。第11题考的是异常捕获的匹配顺序throw new Exc1();Exc1是Exc0的子类catch (Exc0) 能捕获子类异常输出 Ex0 caught。注意如果先写catch (Exception e)Exc1也会被匹配Java按catch块从上到下找第一个能匹配的异常类型。实际开发里子类异常catch要写在父类异常前面否则编译器直接报已经捕获到错误。第17、18、20题是线程和关键字的基础run()定义线程执行体start()启动线程synchronized用来加互斥锁static修饰的方法可以通过类名直接调用。第19题问构造函数描述错误的是A构造函数的返回类型只能是void型。构造函数压根不能声明返回类型写void也是语法错误这是初学Java最容易被误导的一句话。第14、15、16题分别对应开闭原则、设计模式两大主题、继承是白盒复用——其中第16题说继承对父类的修改不会影响到子类是错的白盒复用的特点恰恰是父类一改子类跟着变。3. 上机题实现字符串排序、递归、SQL和单例的标准答案3.1 字符串100,10,1,0,15,20排序先转int再排第21题要求把100,10,1,0,15,20中的数字升序排列输出0,1,10,15,20,100。这道题最直接的坑是字符串自带排序是字典序直接Arrays.sort会排成0,1,10,100,15,20完全不对。必须先把字符串拆成数字再排序import java.util.Arrays; public class Test1 { public static String sort(String args) { String[] parts args.split(,); int[] nums new int[parts.length]; for (int i 0; i parts.length; i) { nums[i] Integer.parseInt(parts[i].trim()); } Arrays.sort(nums); StringBuilder sb new StringBuilder(); for (int i 0; i nums.length; i) { if (i 0) { sb.append(,); } sb.append(nums[i]); } return sb.toString(); } public static void main(String[] args) { String s 100,10,1,0,15,20; System.out.print(sort(s)); } }逻辑说明split(,)按逗号切分得到字符串数组trim()是保险操作防止输入数据里带空格Integer.parseInt把每个字符串转成int这样排序才是数值序最后用StringBuilder拼接避免循环里用产生大量临时对象。参数说明方法签名里的args就是入参字符串和main的String[] args不冲突但建议换名input更清晰。如果面试官追问排序复杂度直接答Arrays.sort对基本类型用的是双轴快速排序平均O(n log n)初级岗位答到这里就够。3.2 斐波那契第30位递归能跑但要有优化意识第22题给了递归实现求数列 1、1、2、3、5、8... 的第30位数public class TestNum { static int computeNum(int index) { return index 2 ? 1 : computeNum(index - 1) computeNum(index - 2); } public static void main(String[] args) { System.out.println(computeNum(30)); } }逻辑说明index 2是递归出口前两位都是1后面每位等于前两位之和。第30位算出来是832040这个数字可以当作自检答案。这段代码能跑但效率很差重复计算量是指数级的。面试官大概率会追问能不能优化。我一般会顺手写一个带缓存的版本static int computeFast(int index, int[] cache) { if (index 2) { return 1; } if (cache[index] ! 0) { return cache[index]; } cache[index] computeFast(index - 1, cache) computeFast(index - 2, cache); return cache[index]; }在main里先int[] cache new int[31];再调用computeFast(30, cache)。空间换时间每个下标只算一次复杂度从O(2^n)降到O(n)。初级岗位能用递归写出正确结果已经及格能主动给出缓存版本就是加分项。3.3 Oracle两张表的SQL三连查里的常见错误与正确写法第23题给了部门表DEPT和员工表EMP三个查询分别考察子查询、分组过滤、连接聚合。先看原题给出的答案第2题写的是select job from EMP where sal2000第3题用的是avg(d.Deptno)这两个都有明显问题。第一问列出薪金比张平多的所有员工姓名。正确写法是用子查询取出张平的薪金再做行比较SELECT Ename FROM EMP WHERE Sal (SELECT Sal FROM EMP WHERE Ename 张平);逻辑说明内层子查询先算出张平的工资2800外层再筛掉自己、留下大于2800的人。需要注意Ename 张平用的是单引号Oracle里字符串常量必须单引号双引号会被当成列名。结果里张平本人不会出现因为2800 2800 不成立。第二问列出最低薪金大于2000的各种职位。关键词是各种职位和最低薪金这是典型的聚合条件SELECT Job FROM EMP GROUP BY Job HAVING MIN(Sal) 2000;逻辑说明先按job分组然后用HAVING MIN(Sal) 2000过滤组。原题答案select job from EMP where sal2000的错误在于where是在分组前对行过滤会把所有工资大于2000的员工列出来然后职位重复而且业务员里王一平虽然个人工资1850不高但另一个业务员孙小平2100是被原答案漏掉的不孙小平2100大于2000会被原答案包含于是结果里就出现了业务员违背了最低薪金大于2000的语义。分组后业务员组的最低薪金是1850不满足条件。HAVING和WHERE的差别是这道题最想考的。第三问列出每个部门的部门名称、员工数量和平均薪水。注意两张表的关联字段是deptno连接条件要写在on里SELECT d.Dname AS 部门名称, COUNT(e.Empno) AS 员工数量, AVG(e.Sal) AS 平均薪水 FROM DEPT d JOIN EMP e ON e.Deptno d.Deptno GROUP BY d.Dname;逻辑说明以部门表为左表员工表为右表按部门编号关联COUNT(e.Empno)统计每个部门的员工数AVG(e.Sal)统计平均薪水。原题答案里avg(d.Deptno)是明显的笔误平均薪水肯定取EMP表的Sal字段而不是部门编号。另外原答案把连接条件写在where里在Oracle里也能跑出内连接效果但规范写法是放到ON子句。如果要求市场部无员工也显示出来就得改成左连接LEFT JOIN EMP e ON e.Deptno d.Deptno初级岗位能想到这里已经很稳。3.4 单例模式先写对再防并发第24题要求实现单例。原题答案已经给了懒汉式的基本框架class Singleton { private static Singleton s; private Singleton() {} public static Singleton getInstance() { if (s null) { s new Singleton(); } return s; } }逻辑说明构造方法私有外部无法直接new静态变量s持有唯一实例getInstance在第一次调用时创建。这段代码单线程下没问题但多线程环境下两个线程同时进入if (s null)可能各new一次。初级笔试能写出这个版本算合格但如果面试官让你保证线程安全我一般会答synchronized方法版class Singleton { private static Singleton s; private Singleton() {} public static synchronized Singleton getInstance() { if (s null) { s new Singleton(); } return s; } }再进一步就是双重检查锁加volatileclass Singleton { private static volatile Singleton s; private Singleton() {} public static Singleton getInstance() { if (s null) { synchronized (Singleton.class) { if (s null) { s new Singleton(); } } } return s; } }写的时候要说清楚外层判空避免每次调用都抢锁内层判空防止并发重复创建volatile保证s的可见性和禁止指令重排。初级岗位不要求写出双检锁但能写出synchronized版本并说明理由就已经把这道题的边界吃透了。4. 笔试避坑记录五条最容易翻车的踩坑经验4.1 现象do-while最后输出-1自己算出来却是0或1第1题我见过好几个人信誓旦旦选0理由是1除以2等于0循环结束i就是0。原因就是后置自减的比较顺序搞反了把i-- 1当成先减后比。解决方法是把后置自减拆开比较时用的是i的旧值比较完才自减。我自己的习惯是做题时在草稿纸上画一张三轮的小表列i的当前值、进入循环后的值、比较结果、自减后的值四列写完答案自然出来。这个习惯也从笔试沿用到了写代码凡是遇到i--参与的判断一律单独一行不写在条件里。4.2 现象for循环里的变量循环外一用就编译失败第6题看上去在考break实际上考作用域。有人算出7有人算出11却没发现System.out.println(i)根本不在for的作用域内。原因是对for的语法结构不敏感for (int i0; ...)里声明的i生命周期从for开始到for结束。解决方法是拿到代码先扫一遍变量声明位置再看循环逻辑。这类题在笔试里出现频率很高不只是JavaC和JavaScript也爱考。我遇到这种代码会先问自己一个问题这段代码能编译吗而不是这段代码输出什么顺序反了就会掉坑。4.3 现象子类构造时static和父类构造器的输出顺序全乱第12题的输出顺序是静态成员、实例成员、父类构造器、子类构造体这个顺序新手普遍记不住。原因在于把类加载和对象创建混为一谈。我的记忆方法是静态成员属于类类第一次被加载时只初始化一次所以最早实例成员属于对象每次new都执行所以紧随其后构造器是对象创建的最后一个步骤父类又先于子类。以后遇到初始化顺序题第一件事就是先找static字段把它从对象创建流程里单独摘出来剩下的再按实例字段→父类构造→子类构造体推。4.4 现象SQL用where筛出所有职位结果面试官直接摇头写最低薪金大于2000的职位时不少人第一反应是where sal2000然后交卷。原因是没有抓住各种职位这个聚合语义最低薪金是一个组级别job级别的条件不是行级别条件。行过滤用where组过滤必须用having。解决方法是看题目里有没有各种每个平均最低这类词有就基本锁定group by加having。这道题还有一个后续追问点为什么业务员职位没出现在结果里因为业务员组的最低薪金1850不满足大于2000。能把业务员排掉的原因讲清楚这道SQL题才算真会。4.5 现象单例写完了被追问多线程下会不会new出两个第24题原答案是非线程安全的懒汉模式。很多人笔试过了面试时被问两个线程同时调用getInstance会发生什么就卡住。原因是他只知道单例的写法不知道为什么这么写。解决方法是把线程安全问题分三层答第一层两个线程同时判空都可能进入if第二层解决办法是synchronized方法或同步块第三层同步块里还要再判空一次否则后进入的线程依然会再new一次。从那以后我教人写单例都强制要求在白板上画出两个线程的执行时序图画得出来才叫会。5. 把一份笔试题变成自查清单做对之后还要能讲清这套题做完如果只是对答案、改错号那价值就只发挥了一半。我习惯把它变成一份按考点分类的自查清单语法类、面向对象类、异常类、并发类、SQL类。每道错题记录三个字段考点一句话、我当时的错误答案、正确思路。比如第1题写后置自减的比较时机第6题写for变量作用域第12题写静态成员先于对象创建这样复盘时不用重看整张卷子。自查清单做完还有一步更狠的挑三道题改写成变体。第6题改成int i; for (i0; i10; i) { if(i6) break; }再输出i结果从编译失败变成7第12题在main里第一行加new Super(9);整个输出顺序都会变第21题把字符串换成带前导零的001,02,10考验parseInt的容错。改完再自己算一遍算对了才算真正掌握了这个考点。最后一步是口头讲题。找一面镜子或者对着录音工具把第10题异常流程、第23题having和where的区别用两分钟讲清楚。讲的时候卡壳的地方就是理解还模糊的地方。这个习惯来自一次真实的笔试复盘我做对了所有选择题面试官让我讲第12题我却从static s1开始就说不利索当场露馅。从那以后我每次刷笔试题都强制自己走一遍复现→改题→讲题的流程。笔试不只是选对答案能讲清楚边界和原因的答案才是真正属于你的答案。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站