棘手的Java面试问题是那些有一些惊喜元素的问题。如果你试图用常识回答一个棘手的问题,你很可能会因为需要一些特定的知识而失败。大多数棘手的Java问题来自于令人困惑的概念,如函数重载和覆盖,多线程,掌握非常棘手,字符编码,检查与未检查的异常和Integer溢出等微妙的Java编程细节。
这里大家可以关注一下我的个人专栏《Java 进阶集中营》,每天会给大家即时分享一个最新的java技术资讯,有优秀的java技术内容,也欢迎分享在我的专栏。
JAVA 进阶集中营zhuanlan.zhihu.com
回答棘手的Java问题最重要的是态度和分析思维,即使你不知道答案也会有所帮助。在这篇Java文章中,我们将看到下面几个Java问题,并且需要更多的Java编程语言知识来正确回答它们。
1:以下Java程序打印了什么?
public class Test {
public static void main(String[] args){
System.out.println(Math.min(Double.MIN_VALUE, 0.0d));
}
}
不像整数,其中MINVALUE为负,Double的MAXVALUE和MIN_VALUE都是正数。Double.MINVALUE是2 ^( - 1074) ,Double常数,其大小是所有的Double值当中最小。因此,与明显的答案不同,此程序将打印0.0,因为Double.MINVALUE大于0.向Java开发人员提出了这个问题,他们有3至5年的经验,令人惊讶的是,近70%的候选人错了。
2:如果在try或catch块上放置return语句或System.exit()会发生什么?最后会阻止执行吗?
这是一个非常流行的棘手的Java问题,它很棘手,因为许多程序员认为无论如何,但finally块将始终执行。这个问题通过在try或catch块中放置一个return语句或从try或catch块调用System.exit()来挑战该概念。在Java中回答这个棘手的问题是,即使你在try块或catch块中放入一个return语句,finally块也会执行,但是如果你从try或catch块调用System.exit(),最后块将无法运行。
3:您可以在Java中覆盖私有或静态方法吗?
如果要在Java中提出技巧问题,方法覆盖是一个很好的主题。无论如何,你不能在Java中覆盖私有或静态方法,如果你在子类中创建一个具有相同返回类型和相同方法参数的类似方法,那么它将隐藏超类方法,这称为方法隐藏。
类似地,您不能覆盖子类中的私有方法,因为它在那里不可访问,您要做的是在子类中创建另一个具有相同名称的私有方法。
4:表达式1.0 / 0.0将返回什么?它会抛出异常吗?任何编译时错误?
虽然Java开发人员知道双原语类型和Double类,但在进行浮点运算时,他们没有足够重视Double.INFINITY,NaN和-0.0以及其他规则来控制涉及它们的算术计算。这个问题的简单答案是它不会抛出ArithmeticExcpetion并返回Double.INFINITY。
另外,请注意,即使x本身是NaN,比较x == Double.NaN也始终求值为false。要测试x是否为NaN,应该使用方法调用Double.isNaN(x)检查给定的数字是否为NaN。如果您了解SQL,那么非常接近NULL。
5:Java是否支持多重继承?
如果C ++可以支持直接的多重继承,那么为什么Java不是Interviewer经常给出的参数。这个问题的答案比它看起来更加微妙,因为Java通过允许接口扩展其他接口来支持Type的多个继承,Java不支持的是多个实现继承。由于现在Java 8的默认方法提供了Java也存在多种行为继承,因此这种区别也变得模糊。
6:如果我们将一个关键对象放在已经存在的HashMap中会发生什么?
HashMap如何在Java中运行。HashMap也是一个在Java中创建令人困惑和棘手的问题的热门话题。这个问题的答案是,如果你再次使用相同的密钥,那么它将替换旧的映射,因为HashMap不允许重复密钥。相同的密钥将产生相同的哈希码,并最终将在桶中的相同位置。
每个存储桶都包含一个Map.Entry对象的链接列表,其中包含Key和Value。现在,Java将从每个条目中获取Key对象,并使用equals()方法与此新键进行比较,如果返回true,则该条目中的value对象将被新值替换。
7:以下Java程序打印了什么?
public class Test {
public static void main(String[] args) throws Exception {
char[] chars = new char[] {'u0097'};
String str = new String(chars);
byte[] bytes = str.getBytes();
System.out.println(Arrays.toString(bytes));
}
}
这个问题的要点在于字符编码以及字符串到字节数组转换的工作原理。在这个程序中,我们首先从一个字符数组创建一个String,它只有一个字符' u0097',之后我们从该String获取字节数组并打印该字节。由于 u0097在字节基本类型的8位范围内,因此猜测str.getBytes()调用将返回包含一个值为-105 ((byte)0x97)的元素的字节数组是合理的。
然而,这不是程序打印的,这就是为什么这个问题很棘手。事实上,程序的输出是依赖于操作系统和语言环境的。在具有美国语言环境的Windows XP上,上述程序打印[63],如果在Linux或Solaris上运行此程序,则会得到不同的值。
要正确回答这个问题,您需要了解Unicode字符在Java字符串值和Java字符串中的表示方式,以及字符编码在String.getBytes()中的作用。
简单来说,将字符串转换为字节数组,Java遍历字符串表示的所有字符,并将每个字符转换为多个字节,最后将字节放在一起。将每个Unicode字符映射到字节数组的规则称为字符编码。因此,如果在编码和解码期间不使用相同的字符编码,则检索到的值可能不正确。当我们调用 str.getBytes() 而不指定字符编码方案时,JVM使用平台的默认字符编码来完成工作。
默认编码方案是操作系统和区域设置相关。在Linux上,它是UTF-8,在Windows上具有美国语言环境,默认编码为Cp1252。这解释了我们在具有美国语言环境的Windows机器上运行此程序所获得的输出。无论使用哪种字符编码方案,Java总是将编码未识别的Unicode字符转换为63,这表示所有编码中的字符U + 003F。
8:以下在Java中实现compareTo()方法有什么问题
public int compareTo(Object o){
Employee emp = (Employee) o;
return this.id - e.id; }
其中id是整数。 好吧,在你保证id总是正面之前,这个Java问题中的三个没有错。当你无法保证id为正或负时,这个Java问题变得棘手。棘手的部分是,如果id变为负数,则减法可能会溢出并产生不正确的结果。
9:你如何确保N线程可以在没有死锁的情况下访问N个资源?
如果您不熟悉编写多线程代码,那么这对您来说是一个非常棘手的问题。即使对于没有真正面临死锁和竞争条件的经验丰富的高级程序员来说,这个Java问题也很棘手。这里的关键点是排序,如果您按特定顺序获取资源并以相反的顺序释放资源,则可以防止死锁。
考虑以下Java代码片段,它初始化两个变量并且两者都不是易失性的,并且两个线程T1和T2正在修改这些值,如下所示,两者都不同步
int x = 0;
boolean bExit = false;
Thread 1 (not synchronized)
x = 1;
bExit = true;
Thread 2 (not synchronized)
if (bExit == true)
System.out.println("x=" + x);
这个问题的答案是肯定的,线程T2可能会打印x = 0.为什么?因为没有对编译器的任何指令,例如synchronized或volatile, bExit = true 可能在编译器重新排序中在x = 1之前出现。此外,x = 1可能在线程2中不可见,因此线程2将加载x = 0。现在,你如何解决它?
向几个程序员提出这个问题时,他们的回答不同,一个人建议让两个线程在一个共同的互斥锁上同步,另一个人说这两个变量都是易变的。两者都是正确的,因为它会阻止重新排序并保证可见性。
但最好的答案是你只需要使 bExit 成为易失性,然后线程2只能打印“x = 1”。x不需要是volatile,因为 当bExit 是volatile 时, 不能在bExit = true之后重新排序x 。