Java 棘手程序 | 棘手的 Java 面试题

2025年3月17日 | 阅读11分钟

棘手的 Java 面试题是指那些需要付出额外努力才能解决的问题。如果我们尝试用常识来回答一个棘手的问题,我们会失败,因为它们需要一些特定的知识。大多数棘手的 Java 问题都来自令人困惑的概念,如循环、多线程、重载、重写等。

有时我们在解决问题时会感到困惑,而问题本身并没有那么难。有时我们随意编写代码,而问题的解决方案却非常简单。

即使我们不知道答案,我们也可以通过运用我们的分析思维来解决问题。在本节中,我们将介绍一些棘手的 Java 程序棘手的面试题

1) 考虑以下代码并猜测输出。

  1. Hello
  2. hello
  3. HELLO
  4. Error
  5. \u000dhello
 

答案:b

解释: 正如我们看到的打印语句,它看起来像被注释掉了,但并非如此。// \u000d 是一个换行符的 Unicode 字符。


2) 考虑以下代码并猜测输出。

  1. 77
  2. 33
  3. 27
  4. 76
  5. 66
 

答案:a

解释: 在程序中,这些运算符只是为了迷惑人。这些运算符既不是前置递增也不是后置递增。该语句可以按以下方式编写和解决:

int i=20+ (+9)- (-12)+ (+4)- (-13)+ (+19);

i=20+9+12+4+13+19

i=77


3) 考虑以下代码并找出输出。

  1. true 只打印一次。
  2. true 只打印两次。
  3. true 只打印三次。
  4. true 只打印四次。
  5. 代码无法编译。
 

答案:c

解释: 因为字符串字面量是从字符串池中使用的。这意味着 s1 和 s2 指向同一个对象并且相等。因此,前两条打印语句打印 true。第三条打印语句打印 false,因为 toString() 方法使用一个方法来计算值,并且它不是来自字符串池。最后一条打印语句再次打印 true,因为 equals() 关注 String 对象的值。


4) 以下代码片段的输出是什么?

  1. abe
  2. abce
  3. abde
  4. abcde
  5. 抛出未捕获的异常。
 

答案:d

解释: 因为代码开始运行并在第 13 行和第 15 行打印 a 和 b。第 16 行抛出异常,在第 17 行捕获。在第 18 行打印 c 之后,finally 块运行并打印 d。然后 try 语句结束,并在第 22 行打印 e。


5) 以下代码的输出是什么以及为什么?

  1. 第 1 行出现编译器错误。
  2. 第 2 行出现编译器错误。
  3. 第 4 行出现编译器错误。
  4. 第 5 行出现编译器错误。
  5. 第 8 行出现编译器错误。
 

答案: e

解释: 局部变量在引用之前需要赋值。选项 E 不正确,因为类变量和实例变量具有默认值并允许引用。a_b 默认为 null 值。选项 A、B、C 和 D 不正确,因为标识符可以以字母、下划线或美元符号开头。如果 a_b 是一个实例变量,代码将编译并输出 0null。


6) 以下代码的输出是什么以及为什么?

  1. [8]
  2. [9]
  3. 类似于 [Ljava.lang.String;@160bc7c0
  4. 抛出异常。
  5. 代码无法编译。
 

答案:b

解释: 数组允许使用匿名初始化器,因为它与声明在同一行。ArrayList 使用 Java 7 以来允许的菱形运算符。它指定类型与左侧的类型匹配,而无需重新输入。添加两个元素后,list 包含 [6, 8]。我们将索引 1 处的元素替换为 9,得到 [6, 9]。最后,我们删除索引 0 处的元素,剩下 [9]。选项 C 不正确,因为数组的输出是类似这样的,而不是 ArrayList。


7) 以下代码的输出是什么以及为什么?

  1. true true
  2. true false
  3. false true
  4. false false
  5. 编译失败
 

答案: e

解释: Unicode 声明必须用单引号括起来:'\u004e'。


8) 以下代码的输出是什么以及为什么?

  1. 内存错误输出
  2. 栈溢出错误
  3. 孤立的 case 错误
  4. 断言错误
  5. IO 错误
 

答案:c

解释: 因为 Java 不允许我们在 case 语句中比较值。由于无效的 switch 语句而发生的错误称为孤立的 case 错误。


9) 输出是什么?

  1. ONE3TWOTHREE7FOURFIVE5
  2. ONE12TWOTHREE7FOURFIVE5
  3. ONE3TWOTHREE34FOURFIVE5
  4. ONETWOTHREEFOURFIVE15
  5. ONE12TWOTHREE34FOURFIVE5
 

答案: e

解释: 因为定义的数字是字符串类型,所以它们不会相加。因此,上面的代码会原样打印该字符串。


10) 输出是什么?

  1. 0
  2. 0.0
  3. Null
  4. 算术异常
  5. 编译错误
 

答案:b

解释: 在 Integer 类中,MIN_VALUE 是负数(即 -231),而 Double 类的 MAX_VALUE 和 MIN_VALUE 都是正数。Double.MIN_VALUE 是 2-1074,一个双精度常量,其绝对值在所有双精度值中最小。

因此,与显而易见的答案不同,这个程序将打印 0.0,因为 Double.MIN_VALUE 大于 0。


11) 评估以下两个语句并找出输出?

  1. 31536000000 和 1726327040
  2. 3.1536e10 和 1.72632704e9
  3. 31536000000L 和 1726327040
  4. 编译时错误
  5. 算术异常
 

答案:a

解释: 计算很简单。第一个语句末尾有 L,表示精确的显式长整型值,即 31536000000。另一方面,第二个语句末尾没有 L,因此它会给出不同的结果值。因为它被视为整数并以整数计算结果。但结果超出了整数范围的边界。因此,它开始截断值,即 1726327040。因此,L 的目的是显式表示长整型值。


12) 考虑以下代码并评估输出?

  1. 1
  2. 2
  3. 1110
  4. 1109
  5. 11
 

答案:b

解释: 我们知道静态块首先执行。因此,x 的后减量值为 1111,前减量值为 1109,两者之差为 2,并将在控制台上打印。请注意,静态块之后的块永远不会执行。


13) 考虑以下代码并评估输出?

  1. Rock
  2. Scissors(剪刀)
  3. 纸张
  4. 语法错误
  5. Exception
 

答案:d

解释: 在上面的程序中,switch 语句在三个可能的选项中随机选择。回想一下,表达式 (int)(3*Math.random()) 的值是 0、1 或 2 中的一个整数,以相等的概率随机选择,因此下面的 switch 语句将以 1/3 的概率将 "Rock"、"Scissors" 或 "Paper" 中的一个值分配给 computerMove。尽管此示例中的 switch 语句是正确的,但此代码段作为一个整体说明了一个微妙的语法错误

我们可能没有发现错误,因为它从人类的角度来看不是错误。计算机报告最后一行是错误,因为变量 computerMove 可能没有被赋值。在 Java 中,只有当变量已被明确赋值时,才合法使用该变量的值。这意味着计算机必须能够仅仅通过查看程序编译时的代码来证明变量必须已被赋值。不幸的是,计算机只有一些简单的规则可以用来进行判断。在这种情况下,它看到一个 switch 语句,其中表达式的类型是 int,并且涵盖的 case 是 0、1 和 2。对于表达式的其他值,computerMove 从未被赋值。因此,计算机认为 computerMove 在 switch 语句之后可能仍然未定义。实际上,这不是真的:0、1 和 2 实际上是表达式 (int)(3*Math.random()) 唯一可能的值,但计算机不够聪明,无法弄清楚这一点。解决此问题最简单的方法是将 case 标签 case 2 替换为 default。

计算机可以看到在所有情况下都为 computerMove 赋值。更普遍地,我们说在程序中给定点变量已被明确赋值,如果从变量声明到代码中的该点的每个执行路径都包含对变量的赋值。此规则考虑了循环、if 语句以及 switch 语句。


14) 如果我们尝试编译并运行它,会发生什么?

  1. 它将编译失败
  2. 运行时错误
  3. 编译并运行,无输出
  4. 编译并运行,打印 Hello
  5. 第 6 行出错
 

答案:d

解释: 程序将编译并成功运行。Test 类是否为空无关紧要。


15) 在双花括号初始化中,第一对花括号和第二对花括号的含义是什么?

  1. 第一组花括号创建一个新的匿名内部类,第二组花括号创建一个像类中静态块一样的实例初始化器。
  2. 第二组花括号创建一个新的匿名内部类,第一组花括号创建一个像类中静态块一样的实例初始化器。
  3. 第一组花括号和第二组花括号既不创建新的匿名内部类,也不创建像类中静态块一样的实例初始化器。
  4. Java 不允许使用双花括号初始化。
  5. 以上都不是
 

答案:a

解释: 第一个花括号创建了一个新的匿名内部类。这些内部类能够访问其父类的行为。因此,在我们的例子中,我们实际上是创建了一个 HashSet 类的子类,因此这个内部类能够使用 put() 方法。

第二组花括号只是实例初始化器。回想一下 Java 核心概念,我们可以轻松地将实例初始化块与静态初始化器关联起来,因为它们具有相似的花括号结构。唯一的区别是静态初始化器用 static 关键字添加,并且只运行一次;无论我们创建多少个对象。


16) 以下哪个语句无限运行?

i. for( ; ; )
ii. for( ; true; )
iii. for( ; false; )
iv. for( ; 2==2; )
v. for(int i=1; i>=1; i++)

  1. 只有 i
  2. ii 和 v
  3. i, ii 和 iii
  4. i, ii, iv 和 v
  5. i, ii, iii, iv 和 v
 

答案:d

解释: 循环 i, ii, iv,v 无限运行,因为第一个 for 循环在条件位置默认选择 true 值,第二个 for 循环指定条件为 true,第四个 for 循环比较值并返回 true,第五个 for 循环在每次迭代时也返回 true。因此,for 循环 i, ii, iv,v 无限运行。


17) 考虑以下程序。

选择正确的输出。

  1. Static Block-1, Static Block-2, Main Method
  2. Static Block-2, Static Block-1, Main Method
  3. Main Method, Static Block-1, Static Block-2
  4. Static Block-1, Main Method, Static Block-2
  5. Main Method, Static Block-2, Static Block-1
 

答案:a

解释: 如果 Java 程序同时包含静态块和 main() 方法,在这种情况下,所有静态块将首先执行,然后是 main() 方法。因此,选项 A 是正确的。


18) 考虑以下程序。

控制台将打印什么?

  1. [Java\\ Python\\ Hadoop]
  2. [Java|Python|Hadoop]
  3. [Java\\|Python\\|Hadoop]
  4. [Java, Python, Hadoop]
 

答案:d

解释: String.split() 方法使用指定的定界符 (|) 分割字符串。以下语句也执行相同的操作。


19) 考虑这个场景。

如果我们在 try 或 catch 块中放置 System.exit(0),那么 finally 块会执行吗?请说明原因。

  1. 这是一个无效的情况。
  2. 它会跳过 finally 块。
  3. JVM 会因 SecurityException 退出,finally 块会执行。
  4. JVM 会在没有 SecurityException 异常的情况下退出,finally 块不会执行。
  5. finally 块可能执行,也可能不执行。
 

答案: e

解释: 通过在 try 或 catch 块中调用 System.exit(0),我们可以跳过 finally 块。System.exit(int) 方法可能会抛出 SecurityException。

如果 System.exit(0) 在不抛出异常的情况下退出 JVM,那么 finally 块将不会执行。但是,如果 System.exit(0) 确实抛出 SecurityException,那么 finally 块将执行。


20) 以下 Java 程序会抛出什么异常?

  1. ArrayIndexOutOfBoundsException
  2. ArrayStoreException
  3. NegativeArraySizeException
  4. NullPointerException
  5. ClassCastException
 

答案:b

解释: ArrayStoreException 是一个运行时异常。数组必须包含相同数据类型的元素。当试图将错误类型的对象存储到对象数组中时,会抛出此异常。换句话说,如果您尝试将整数对象存储到字符串数组中,您将得到 ArrayStoreException。