Java基础练习题精讲:语法细节、JVM机制与面试自测
简介这是一份面向Java初学者与备考者的基础练习题附答案文档针对main方法定义、JVM执行特点、语言特性、符号与表达式、基本数据类型、运算符、控制结构、方法、异常处理等核心考点设计了系统性选择题可帮助读者快速检验知识掌握程度并查漏补缺。资源包共1个文件类型为doc大小约172KB内容按知识点模块化编排便于按章节刷题与对照学习。已有798人学习下载。文档后附详细答案与关键解析不仅给出正确选项还对编译运行结果、数据类型转换、短路逻辑、位运算等易错点进行了补充说明适合课堂练习、考前复习或自学巩固使用。通过完成这些基础题目可加深对Java语法与编程规范的理解为后续Android应用开发、Web开发、企业软件开发等方向打下扎实基础。1. 用这套 Java 基础题先给语法功底打层底把这套 Java 基础练习题发给一个刚自学两个月的新人他 20 分钟交卷错了快一半。错的地方不是 API 记不住而是 main 方法签名、012 这种字面量、x--1 y1 的求值顺序全是语法细节题。这套题单从简单程序调试、符号与表达式、分支、循环、方法、数组到类与对象七个板块都带答案适合三类人准备 Java 面试的人拿来做自测带新人的老手用来摸底自学者用来查漏补缺。下面按考点拆开讲把每道题为什么这么出、答案怎么判断、坑在哪儿说清楚。建议先独立做一遍再对答案错题比做对的题更能暴露问题而且这套题的板块顺序天然是一份复习目录哪个板块错得多接下来就补哪里。2. main 方法签名与 JVM 执行特点从六道送分题看失分点2.1 为什么 main 方法签名这么容易写错第一板块六道题里有五道围绕 Java 程序的基本结构最典型的是 main 方法的定义。标准签名是 public static void main(String[] args)五个部分各有作用public 让 JVM 能调用它static 表示不需要先创建对象就能执行void 表示入口方法不返回值String[] 用来接收命令行参数。题里 B 选项 public static void main(String args) 少了一对方括号声明的是单个 String 变量而不是数组E 选项把返回类型改成 booleanJVM 找不到签名直接拒绝。参数名倒是可以随便写args、argv、a 都行类型必须是 String[]。String args[] 和 String[] args 是等价写法C 选项两种都符合。有个细节面试官常问main 方法参数写错时编译器并不报错。因为从 Java 语言角度看public static void main(String argv) 只是一个普通静态方法javac 不会拦你是 JVM 在启动时按方法描述符查找入口失败。可以亲手还原这个过程public class MainCheck { public static void main(String argv) { System.out.println(Hello); } }先用 javac MainCheck.java 编译完全通过再用 java MainCheck 运行JVM 直接报错找不到 main 方法。JVM 期望的方法描述符是 ([Ljava/lang/String;)V也就是参数为 String 数组、返回 void参数写成单个 String 后描述符变成 (Ljava/lang/String;)V匹配不上。理解描述符这个概念比死背签名更能应对变体题比如把 main 改成 private 或去掉 static编译能过运行同样失败。2.2 JVM 执行特点与语言特点不能混为一谈第一板块第三题问哪些不属于 JVM 执行特点第四题问哪些不属于 Java 语言特点。两道题放一起考的是平台和语言的分层。JVM 执行字节码时确实会做异常处理、线程调度、动态链接这些是 JVM 运行时的行为而分布式、面向对象属于语言和标准库层面的描述。「简单易学」跟 JVM 执行一点关系没有「编译执行」对 Java 也不准确Java 是先把源码编译成字节码再由 JVM 解释执行或 JIT 编译成本地代码两段式混合不能叫纯编译执行。选项归属判断依据异常处理JVM 执行特点字节码中有 athrow 指令方法表带异常表多线程JVM 执行特点线程调度与同步由 JVM 和底层 OS 配合动态链接JVM 执行特点符号引用在类加载时解析为直接引用简单易学都不是语言设计目标不是执行行为分布式Java 语言生态RMI、网络库属于 J2SE 能力面向对象Java 语言类、对象、继承等语法特性编译执行都不是Java 是先编译为字节码再执行的两段式对经验更丰富的开发者来说这道题的增量信息在动态链接。用 javap -verbose 反编译任意 class 文件常量池里全是 CONSTANT_Utf8 形式的符号引用类加载阶段才替换为直接引用。这一机制支撑了类加载器模型也解释了为什么同一个类可以被不同 ClassLoader 重复加载产生多个版本。2.3 字符串拼接与编译期常量第一板块最后两题是字符串输出的口算。System.out.println(x5) 输出 x5加号遇到 String 操作数就执行拼接而不是算术相加。goodmorning 同样输出 goodmorning没有空格。隐藏考点是字符串字面量的拼接在编译期就完成了class 文件里直接存放 goodmorning 常量运行时不再做拼接计算。这类编译期常量优化在后面还会碰到比如 switch 的 case 条件里写 21 是合法的编译器在编译期就算出 3。3. 运算符、字面量与类型转换口算要按提升规则来3.1 二元数值提升int 加 double 结果必须是 double第二板块第一题问 int 和 double 做加法得到什么类型答案是 double。Java 的二元算术运算遵循数值提升规则byte、short、char 参与运算先提升为 int两个操作数类型不同时向表达范围更大的那一边提升。优先级链大致是 byte/short/char → int → long → float → double谁靠右谁获胜。同理byte 乘 short 的结果从 int 起算可能落到的类型是 int、long、float、double不可能回到 byte、short 或 char所以多选那题选 E。常见误解是以为结果还能是 byte 或 short实际上一个算术表达式涉及 byte 和 short 时最小结果类型也是 int。字符串拼接是另一个口算热点。第二卷第七题给了四个 println实际是考加号的结合顺序System.out.println( 2 3); // 23 System.out.println(2 3); // 5 System.out.println(2 3 ); // 5 System.out.println(2 3); // 23规律是表达式从左到右计算一旦某个操作数是 String后面的加号全部变成字符串拼接。第一个表达式先算 2 得到 2再拼接 3 得到 23第三个表达式先算 2 3 得到 5再与空串拼接得到 5。原题选项把第一个结果写作 23严格说少了前面那个空格面试口述时建议主动指出这一点反而显得对细节敏感。3.2 前导 0 是八进制012 不等于 12第二板块第三题是经典陷阱int x 012 输出什么。Java 里以 0 开头的整数字面量是八进制012 等于 1×82也就是十进制的 10。整数字面量还有十六进制 0x 开头、二进制 0b 开头。这道题的教训是写代码时不要在十进制数前补 0既影响可读性又容易让读代码的人误解成八进制。字面量写法类型十进制值或说明012int八进制1×82 10010int八进制80x1Fint十六进制310b1010int二进制102.8Ffloat小数字面量默认 double必须加 F99Sshort合法short 字面量后缀同一板块的 010|4 也是这个考点010 按八进制读是 88|4 按位或得到 12选 D。另外 int z 015 因为前导 0 表示八进制而合法char c 17c 反而非法Java 没有 char 字面量后缀这种东西。float f 2.8 编译报错因为 2.8 是 double 字面量窄化为 float 必须写 2.8fboolean b null 更不可能boolean 只有 true、false 两个值。这些零散规则单独记都简单放在选择题里就是用来淘汰凭感觉做题的人。3.3 自增、短路与强制转换第二板块第二题是自增运算符的经典组合int a 2; System.out.print(a); // 先取值 2再自增a 变为 3 System.out.print(a); // 当前 a 是 3 System.out.print(a); // 先自增到 4再输出 4结果连起来是 234。判断口诀a 这个表达式的值是自增前的值a 这个表达式的值是自增后的值。短路逻辑更需要展开。第二卷第九题问 false grade 70 和 false grade 70 各自计算了哪些表达式 是短路与左边为 false 时右边不会执行 是位与用在布尔表达式上时两边都会执行。所以 grade 70 只在第二条语句中被计算。同样方法分析分支板块的短路题int x1,y1,z1; if (x--1 y1 || z1) 打印什么。从左到右x-- 先比较 x1 为 truex 随后变成 0 不能短路继续算 y1比较 y1 为 truey 变成 2整个左半部分为 true|| 短路z 不执行z 保持 1。输出 x0,y2,z1选 A。有逻辑表达式就先找短路点再检查有没有副作用顺序不能乱。赋值转换那题考的是窄化必须强转。long 赋给 int、double 赋给 float 都需要显式 castchar 赋给 int 是宽化不需要int 赋给 char 反而要 cast因为类型不同且值域不兼容。常量 3 赋给 byte 合法因为 3 在 byte 的 -128 到 127 区间内属于编译期常量赋值特例myFloat 3 合法因为 int 到 float 是隐式转换myDouble 3.0 合法右边本来就是 double。相类似的还有 String 的复合赋值s i 合法结果变成拼接后的字符串而 i s 编译错误String 不能隐式转回 int。位运算那道题顺带讲清楚右移差异a8 左移两位是 32-8 右移一位是算术右移补符号位结果是 -4换成无符号右移 才是 2147483644。看到对负数做 时先想「补的是符号位」就能避开选项里用无符号结果冒充的干扰项。4. 分支与循环短路求值、fall-through 与循环边界的判断4.1 三元运算符的类型提升value is 9.0 而不是 9分支板块第二题int x4println(value is ((x4) ? 99.99 : 9))。条件为 false三元表达式取 9但 99.99 是 double两个分支类型不一致时按二进制数值提升int 9 被提升为 double 9.0最终输出 value is 9.0选 C。这类题型和表达式板块的 int 加 double 是同一个原理混合类型运算时窄类型向宽类型靠齐。面试时我一般把这个题改写成 x4 ? yes : 9两个分支一个 String 一个 int结果类型会变成 Object输出时再按字符串拼接规则处理考法更刁钻但原理一致。4.2 if-else 的执行路径与赋值陷阱分支板块第九题问 a 取何值时输出 test2先看原代码if (a 4) System.out.println(test1); else if (a 9) System.out.println(test2); else System.out.println(test3);答案是没有任何可能选 E。原因是 else if 挂在 else 分支上a4 成立时输出 test1 并跳过整个 else 结构a4 不成立时 a 必然小于等于 4后面的 a9 永远不可能成立。这道题考的是执行路径而不是语法。范围分段应该从最严格条件开始排if (a 9) System.out.println(test2); else if (a 4) System.out.println(test1); else System.out.println(test3);分支板块还有两道赋值陷阱题。if (b i j) 是合法的ij 先算出布尔值 true再赋给 boolean 变量 b赋值表达式的值就是 true所以输出 True。而 if (i2) 编译出错因为 int 赋值表达式的结果是 int不能作为布尔条件。两者的分界线在于赋值表达式的结果类型必须恰好是 boolean。4.3 switch 的 fall-throughcase 2 后面会继续执行吗分支板块第六题j2 进入 switchcase 2 打印 value is two但没有 break直接落入下一个 case 的代码继续打印 value is three遇到 break 才退出。case 21 作为常量表达式完全合法编译器在编译期就算出 3。输出是 value is two 后跟 value is three选 D。fall-through 在 Java 面试八股文里出现率很高需要记住switch 不会在每个 case 后自动隔断匹配之后会一直往下执行到你主动 break 或 switch 结束。另一个考点是 case 合并。第十题里 case 2 和 case 3 共用同一段输出代码x 为 2 或 3 都输出 Two。循环板块的 switch(0) 也用了 fall-throughfor(i10;i0;i--) 一次都不执行j 保持 0进入 case 0 执行 jj1 后没有 break继续执行 case 1 的 jj2 才 break最终 j3。做这类题先在草稿上标出每个 case 有没有 break再顺着执行路径往下串。提示写 switch 时每个 case 结尾的 break 不是可选项。如果 case 之间故意共用逻辑必须用注释标明 fall-through 是有意为之否则后续维护的人会当成 bug 补上 break。4.4 循环边界循环结束后的循环变量到底是多少循环板块好几道题都在考循环的终止边界。最典型的是第一题int f 1, k; for (k 2; k 5; k) f * k; System.out.println(k);循环体执行了 k2、3、4 三次最后一次执行完 k 使 k 变成 5再判断 55 为 false 退出所以循环外打印 k 是 5选 C。容易误选 4 的人是把循环变量当前值和循环次数混在一起。注意代码里 f*k 后面跟了双分号多出的分号只是空语句不影响执行。第三题的 for (int i0,j10; total30; ...) 更直接total 初始为 0条件 total30 一开始就不成立循环体一次都不执行直接输出 Total 0。而 do-while 相反先执行一次再判断卷四第四题 do { ii; j--; } while(j0) 会执行两次i 变成 2选 C。循环板块第七题是 do-while 加自增的混合i1,j10每次比较 i --j四轮比较分别是 19、28、37、46全部为 false之后 i5 不再满足 i5 退出最终 i5、j6。第九题则是 break 的落点i3 时 break 跳出循环此时 i3所以先打印 012 再打印当前 i 的值 3结果是 0123。题做多了会发现这类题的共同结构循环变量的最终值由最后一次自增和条件判断共同决定别只数循环次数。三层 for 循环那题直接找取值范围交集i 的范围 0..2j 的范围 1..3k 的范围 2..4三者相等的交集只有 i2、j2、k2 一个点输出一次 2。不要一层层模拟完整过程嵌套循环题先列取值范围再找公共部分效率高得多。双层累加题第八题则适合分段求和i1 时 j 从 10 到 4 累加得到 49i2 时 j 从 10 到 7 的每位乘 2 得到 68i3 时只有 j10 一项乘 3 得 30总计 147硬模拟反而容易加错。4.5 带标签的跳转怎么结束外层循环卷四第二题在双层循环里插入代码以结束外循环正确选项是 break outer。带标签的 break 和 continue 可以跳出任意层循环标签必须放在循环语句之前并用冒号分隔。continue outer 会跳到外层循环的下一次迭代continue middle 和 break middle 都只影响内层循环。一个最小演示outer: for (int x 0; x 3; x) { for (int y 0; y 3; y) { if (y 1) break outer; // 两层循环一起退出 System.out.println(x , y); } }这段代码只输出 0,0 一行y1 时 break outer 直接跳出两层循环。标签跳转性能上没有额外开销但可读性一般实际项目里更常见的做法是提取方法用 return 结束双层遍历。不过选择题和面试题仍然会直接考标签语义知道 break 和 continue 配标签时各自的落点很有必要。控制流考点常见错法判断方法三元类型提升以为输出 9两个分支类型不同先提升再输出else if 执行路径以为 a9 会输出 test2else if 只在前面条件不成立时检查switch fall-through漏掉 case 间穿透从匹配 case 连到 break 或 switch 结束for 循环退出值以为是最后一次执行值自增后再判断条件不满足才退出标签跳转以为 break 只跳内层标签标在哪层就跳出哪层5. 方法、递归与数组值传递、默认初始化与 static 陷阱5.1 值传递基本类型、对象与 String 三种情况要分开记方法板块前四题放在一起就是一个完整的值传递实验。第一题基本类型static void change(int m) { m 2; } int x 5; change(x); x; System.out.println(x); // 6int 参数是值拷贝方法里加 2 不影响实参x 的最终值只取决于 x。第三题 String 同理change(String m) 里执行 mm2 只是把局部引用指向新字符串外部 x 仍指向 hello输出 hello。String 不可变在这里是关键前提引用参数被重新赋值时原引用不会跟着动。第二题是对象引用static void change(Test m) { m.x 2; } Test t new Test(); t.x; // x 为 6 change(t); // m 和 t 指向同一个对象x 变为 8 System.out.println(t.x); // 8引用类型传的是对象地址的副本两个引用指向同一个堆对象方法内修改成员变量会真实改变对象状态。第四题的坐标交换更直观方法内交换的是形参副本回到调用处实参原封不动输出 (5,3) (3,5) (5,3)。判断口诀是方法内修改参数本身不影响实参方法内修改对象的成员变量堆上的状态才变。场景方法内行为调用处实参基本类型 int修改形参 m不变对象引用 Test修改 m.x对象成员变化String 引用m 指向新字符串原引用仍指向原对象5.2 递归展开method(2, k) 是一个幂函数方法板块第五题给了一个经典递归public static int method(int n, int m) { if (m 0) return 1; else return n * method(n, m - 1); }method(2,k) 就是 2 的 k 次方递归展开是 2 * 2 * ... * 1。题目求 k0 到 10 的 method(2,k)-1 之和即 (2 的 0 次方减 1) 加到 (2 的 10 次方减 1)。等比数列求和2 的 11 次方减 1 等于 2047再减去 11 个 1得到 2036。递归题的通用做法是先确定终止条件和递推表达式再套公式不要一层层手算。第六题的 method(m) 考实参求值时机后置自增传进方法的是旧值 0 和 1方法里打印这两个值循环结束后 m 是 2整体输出 012。5.3 数组默认初始化与命令行参数数组板块的默认值题是高频送分题。new int[5] 创建后所有元素都是 0static int arr[] new int[10] 作为成员变量arr[1] 同样是 0char[100] 的 c[50] 是 \u0000 空字符对象数组如 String[] 默认全是 null。这些默认值由 JVM 在堆上分配数组时统一赋值和局部变量必须显式初始化是两回事。数组边界的题里如果循环写成 长度就会抛 ArrayIndexOutOfBoundsException这是数组题最常见的报错场景面试时被问到「数组越界通常在什么情况下出现」答案就是循环边界多写了一等号。命令行参数题值得展开。java MyTest 1 2 3 带引号Shell 把整段当成一个参数传给 JVMargs[0] 的值是中间带空格的字符串而不是拆开的 1、2、3。不加引号时 Shell 才会按空格拆成三个参数。数组长度用的是 length 属性而非 length() 方法ia.length 是字段访问不是方法调用。数组移位题是循环和数组的合体seeds 初始为 {1,2,3,4,6,8}整体左移三次。第一次变成 {2,3,4,6,8,8}第二次 {3,4,6,8,8,8}第三次 {4,6,8,8,8,8}输出前五个是 4 6 8 8 8。这类题的陷阱在于左移后尾部元素重复容易和原数组混淆模拟时每一轮都完整写一遍当前数组状态不要只看头几个元素。5.4 static 与构造方法类级别的坑类与对象板块里static 字段和构造方法是最容易丢分的两点。static int k3 属于类本身两个实例 x1、x2 同时改它共享同一份数据x1.k 后 k4x2.k 后 k5main 里局部变量 k 不影响静态 k最终 x1.k 是 5。同一板块第三题static int m 默认值为 0所以 if (m0) 走真分支输出静态 k 的当前值 4。实例字段 int value 默认也是 0My x new My() 创建后 x 非空输出 x.value 是 0 而不是 null。static 方法的限制更大static 方法不能直接访问实例字段Scope 类里静态 amethod 打印实例变量 i直接编译错误。构造方法的坑藏在签名里void Q21() 和类名同名但有返回值它是普通方法不是构造器类里定义了 Q21(int i) 之后编译器不再提供默认无参构造器new Q21() 编译不过。识别技巧是构造方法不能有任何返回类型包括 void长着类名但有返回类型的都是普通方法。6. 把错题清单改造成 Java 面试追问题库6.1 从选择题到追问句一道题变五个问题这套题单最值得利用的方式不是做一遍而是把每道错题改写成面试官会问的追问句。main 方法题可以问成args 参数名能改成别的吗main 没有 static 会怎样main 方法抛异常由谁处理String args 和 String[] args 有区别吗一个知识点扩成五个问题Java 面试题最常考的就是这种签名级细节。做错的选择题本身就是最好的素材错因往往就是知识盲区把「选错了 C」改写成「为什么 C 不合法」比反复看答案解析有效得多。6.2 建议的三步自测法实践上我建议分三步。第一步限时做题每 20 题一组控制在 25 分钟内模拟笔试节奏。第二步把错题按板块归到一个 md 文件里每题改写成一个问句例如「012 的十进制值是多少」「 和 对右侧表达式的执行有什么不同影响」「switch 里 case 2 后不写 break 会发生什么」。第三步隔天只看问句列表不看选项直接口述答案坚持两周后语法底子和面试八股文的覆盖面都会明显比单纯刷题扎实。口述时主动说出判断依据比如「八进制 012 等于十进制 10因为 1×82」而不是只报一个答案。验证方法可以更简单把核心考点压缩成一页速查纸包括 main 签名五要素、数值提升链、短路逻辑、switch 的 break、数组默认值、值传递规则每周末对着速查纸过一遍能不看提示默写出来就算过关。这套题单覆盖的七个板块正好对应这些考点错题本和速查页配合使用比反复重做原题效率高。最后留一个操作建议把每道错题对应的 Java 代码片段存成 IDE 代码模板每次新建文件时自动出现随手改几个数字跑一遍验证记忆。本文还有配套的精品资源点击获取