专注Java教育14年 全国咨询/投诉热线:400-8080-105
动力节点LOGO图
始于2009,口口相传的Java黄埔军校
首页 学习攻略 Java学习 Java编程:挑战5个最难回答的Java面试题

Java编程:挑战5个最难回答的Java面试题

更新时间:2019-08-13 13:50:43 来源:动力节点 浏览2315次

  1.为什么等待和通知是在Object类而不是Thread中声明的?


  一个棘手的Java问题,如果Java编程语言不是你设计的,你怎么能回答这个问题呢。Java编程的常识和深入了解有助于回答这种棘手的Java核心方面的面试问题。


  为什么wait,notify和notifyAll是在Object类中定义的而不是在Thread类中定义


  这是有名的Java面试问题,招2~4年经验的到高级Java开发人员面试都可能碰到。


  这个问题的好在它能反映了面试者对等待通知机制的了解,以及他对此主题的理解是否明确。就像为什么Java中不支持多继承或者为什么String在Java中是final的问题一样,这个问题也可能有多个答案。


  为什么在Object类中定义wait和notify方法,每个人都能说出一些理由。从我的面试经验来看,wait和nofity仍然是大多数Java程序员最困惑的,特别是2到3年的开发人员,如果他们要求使用wait和notify,他们会很困惑。因此,如果你去参加Java面试,请确保对wait和notify机制有充分的了解,并且可以轻松地使用wait来编写代码,并通过生产者-消费者问题或实现阻塞队列等了解通知的机制。


  为什么等待和通知需要从同步块或方法中调用,以及Java中的wait,sleep和yield方法之间的差异,如果你还没有读过,你会觉得有趣。为何wait,notify和notifyAll属于Object类?为什么它们不应该在Thread类中?以下是我认为有意义的一些想法:


  1)wait和notify不仅仅是普通方法或同步工具,更重要的是它们是Java中两个线程之间的通信机制。对语言设计者而言,如果不能通过Java关键字(例如synchronized)实现通信此机制,同时又要确保这个机制对每个对象可用,那么Object类则是的正确声明位置。记住同步和等待通知是两个不同的领域,不要把它们看成是相同的或相关的。同步是提供互斥并确保Java类的线程安全,而wait和notify是两个线程之间的通信机制。


  2)每个对象都可上锁,这是在Object类而不是Thread类中声明wait和notify的另一个原因。


  3)在Java中为了进入代码的临界区,线程需要锁定并等待锁定,他们不知道哪些线程持有锁,而只是知道锁被某个线程持有,并且他们应该等待取得锁,而不是去了解哪个线程在同步块内,并请求它们释放锁定。


  4)Java是基于Hoare的监视器的思想。在Java中,所有对象都有一个监视器。


  线程在监视器上等待,为执行等待,我们需要2个参数:


  一个线程


  一个监视器(任何对象)


  在Java设计中,线程不能被指定,它总是运行当前代码的线程。但是,我们可以指定监视器(这是我们称之为等待的对象)。这是一个很好的设计,因为如果我们可以让任何其他线程在所需的监视器上等待,这将导致“入侵”,导致在设计并发程序时会遇到困难。请记住,在Java中,所有在另一个线程的执行中侵入的操作都被弃用了(例如stop方法)。


  2.为什么Java中不支持多重继承?


  我发现这个Java核心问题很难回答,因为你的答案可能不会让面试官满意,在大多数情况下,面试官正在寻找答案中的关键点,如果你提到这些关键点,面试官会很高兴。在Java中回答这种棘手问题的关键是准备好相关主题,以应对后续的各种可能的问题。


  这是非常经典的问题,与为什么String在Java中是不可变的很类似;这两个问题之间的相似之处在于它们主要是由Java创作者的设计决策使然。


  为什么Java不支持多重继承,可以考虑以下两点:


  1)第一个原因是围绕钻石形继承问题产生的歧义,考虑一个类A有foo()方法,然后B和C派生自A,并且有自己的foo()实现,现在D类使用多个继承派生自B和C,如果我们只引用foo(),编译器将无法决定它应该调用哪个foo()。这也称为Diamond问题,因为这个继承方案的结构类似于菱形,见下图:


  image.png


  即使我们删除钻石的顶部A类并允许多重继承,我们也将看到这个问题含糊性的一面。如果你把这个理由告诉面试官,他会问为什么C++可以支持多重继承而Java不行。嗯,在这种情况下,我会试着向他解释我下面给出的第二个原因,它不是因为技术难度,而是更多的可维护和更清晰的设计是驱动因素,虽然这只能由Java言语设计师确认,我们只是推测。维基百科链接有一些很好的解释,说明在使用多重继承时,由于钻石问题,不同的语言地址问题是如何产生的。


  2)对我来说第二个也是更有说服力的理由是,多重继承确实使设计复杂化并在转换、构造函数链接等过程中产生问题。假设你需要多重继承的情况并不多,简单起见,明智的决定是省略它。此外,Java可以通过使用接口支持单继承来避免这种歧义。由于接口只有方法声明而且没有提供任何实现,因此只有一个特定方法的实现,因此不会有任何歧义。


  3.为什么Java不支持运算符重载?


  另一个类似棘手的Java问题。为什么C++支持运算符重载而Java不支持?有人可能会说+运算符在Java中已被重载用于字符串连接,不要被这些论据所欺骗。


  与C++不同,Java不支持运算符重载。Java不能为程序员提供自由的标准算术运算符重载,例如+,-,*和/等。如果你以前用过C++,那么Java与C++相比少了很多功能,例如Java不支持多重继承,Java中没有指针,Java中没有引用传递。另一个类似的问题是关于Java通过引用传递,这主要表现为Java是通过值还是引用传参。虽然我不知道背后的真正原因,但我认为以下说法有些道理,为什么Java不支持运算符重载。


  1)简单性和清晰性。清晰性是Java设计者的目标之一。设计者不是只想复制语言,而是希望拥有一种清晰,真正面向对象的语言。添加运算符重载比没有它肯定会使设计更复杂,并且它可能导致更复杂的编译器,或减慢JVM,因为它需要做额外的工作来识别运算符的实际含义,并减少优化的机会,以保证Java中运算符的行为。


  2)避免编程错误。Java不允许用户定义的运算符重载,因为如果允许程序员进行运算符重载,将为同一运算符赋予多种含义,这将使任何开发人员的学习曲线变得陡峭,事情变得更加混乱。据观察,当语言支持运算符重载时,编程错误会增加,从而增加了开发和交付时间。由于Java和JVM已经承担了大多数开发人员的责任,如在通过提供垃圾收集器进行内存管理时,因为这个功能增加污染代码的机会,成为编程错误之源,因此没有多大意义。


  3)JVM复杂性。从JVM的角度来看,支持运算符重载使问题变得更加困难。通过更直观,更干净的方式使用方法重载也能实现同样的事情,因此不支持Java中的运算符重载是有意义的。与相对简单的JVM相比,复杂的JVM可能导致JVM更慢,并为保证在Java中运算符行为的确定性从而减少了优化代码的机会。


  4)让开发工具处理更容易。这是在Java中不支持运算符重载的另一个好处。省略运算符重载使语言更容易处理,这反过来又更容易开发处理语言的工具,例如IDE或重构工具。Java中的重构工具远胜于C++。


  4.为什么String在Java中是不可变的?


  我最喜欢的Java面试问题,很棘手,但同时也非常有用。一些面试者也常问这个问题,为什么String在Java中是final的。


  字符串在Java中是不可变的,因为String对象缓存在String池中。由于缓存的字符串在多个客户之间共享,因此始终存在风险,其中一个客户的操作会影响所有其他客户。例如,如果一段代码将String“Test”的值更改为“TEST”,则所有其他客户也将看到该值。由于String对象的缓存性能是很重要的一方面,因此通过使String类不可变来避免这种风险。


  同时,String是final的,因此没有人可以通过扩展和覆盖行为来破坏String类的不变性、缓存、散列值的计算等。String类不可变的另一个原因可能是由于HashMap。


  由于把字符串作为HashMap键很受欢迎。对于键值来说,重要的是它们是不可变的,以便用它们检索存储在HashMap中的值对象。由于HashMap的工作原理是散列,因此需要具有相同的值才能正常运行。如果在插入后修改了String的内容,可变的String将在插入和检索时生成两个不同的哈希码,可能会丢失Map中的值对象。


  如果你是印度板球迷,你可能能够与我的下一句话联系起来。字符串是Java的VVSLaxman,即非常特殊的类。我还没有看到一个没有使用String编写的Java程序。这就是为什么对String的充分理解对于Java开发人员来说非常重要。


  String作为数据类型,传输对象和中间人角色的重要性和流行性也使这个问题在Java面试中很常见。


  为什么String在Java中是不可变的是Java中最常被问到的字符串访问问题之一,它首先讨论了什么是String,Java中的String如何与C和C++中的String不同,然后转向在Java中什么是不可变对象,不可变对象有什么好处,为什么要使用它们以及应该使用哪些场景。


  这个问题有时也会问:“为什么String在Java中是final的”。在类似的说明中,如果你正在准备Java面试,我建议你看看《Java程序员面试宝典(第4版)》,这是高级和中级Java程序员的优秀资源。它包含来自所有重要Java主题的问题,包括多线程,集合,GC,JVM内部以及Spring和Hibernate框架等。


  正如我所说,这个问题可能有很多可能的答案,而String类的唯一设计者可以放心地回答它。我在JoshuaBloch的EffectiveJava书中期待一些线索,但他也没有提到它。我认为以下几点解释了为什么String类在Java中是不可变的或final的:


  1)想象字符串池没有使字符串不可变,它根本不可能,因为在字符串池的情况下,一个字符串对象/文字,例如“Test”已被许多参考变量引用,因此如果其中任何一个更改了值,其他参数将自动受到影响,即假设

image.png

  现在字符串B调用"Test".toUpperCase(),将同一个对象改为“TEST”,所以A也是“TEST”,这不是期望的结果。


  下图显示了如何在堆内存和字符串池中创建字符串。


  image.png

  2)字符串已被广泛用作许多Java类的参数,例如,为了打开网络连接,你可以将主机名和端口号作为字符串传递,你可以将数据库URL作为字符串传递,以打开数据库连接,你可以通过将文件名作为参数传递给FileI/O类来打开Java中的任何文件。如果String不是不可变的,这将导致严重的安全威胁,我的意思是有人可以访问他有权授权的任何文件,然后可以故意或意外地更改文件名并获得对该文件的访问权限。由于不变性,你无需担心这种威胁。这个原因也说明了,为什么String在Java中是最终的,通过使java.lang.Stringfinal,Java设计者确保没有人覆盖String类的任何行为。


  3)由于String是不可变的,它可以安全地共享许多线程,这对于多线程编程非常重要.并且避免了Java中的同步问题,不变性也使得String实例在Java中是线程安全的,这意味着你不需要从外部同步String操作。关于String的另一个要点是由截取字符串SubString引起的内存泄漏,这不是与线程相关的问题,但也是需要注意的。


  4)为什么String在Java中是不可变的另一个原因是允许String缓存其哈希码,Java中的不可变String缓存其哈希码,并且不会在每次调用String的hashcode方法时重新计算,这使得它在Java中的HashMap中使用的HashMap键非常快。简而言之,因为String是不可变的,所以没有人可以在创建后更改其内容,这保证了String的hashCode在多次调用时是相同的。


  5)String不可变的绝对最重要的原因是它被类加载机制使用,因此具有深刻和基本的安全考虑。如果String是可变的,加载“java.io.Writer”的请求可能已被更改为加载“mil.vogoon.DiskErasingWriter”.安全性和字符串池是使字符串不可变的主要原因。顺便说一句,上面的理由很好回答另一个Java面试问题:“为什么String在Java中是最终的”。要想是不可变的,你必须是最终的,这样你的子类不会破坏不变性。你怎么看?


  5.为什么char数组比Java中的String更适合存储密码?


  另一个基于String的棘手Java问题,相信我只有很少的Java程序员可以正确回答这个问题。这是一个真正艰难的核心Java面试问题,并且需要对String的扎实知识才能回答这个问题。


  这是最近在Java面试中向我的一位朋友询问的问题。他正在接受技术主管职位的面试,并且有超过6年的经验。如果你还没有遇到过这种情况,那么字符数组和字符串可以用来存储文本数据,但是选择一个而不是另一个很难。但正如我的朋友所说,任何与String相关的问题都必须对字符串的特殊属性有一些线索,比如不变性,他用它来说服访提问的人。在这里,我们将探讨为什么你应该使用char[]存储密码而不是String的一些原因。


  字符串:


  1)由于字符串在Java中是不可变的,如果你将密码存储为纯文本,它将在内存中可用,直到垃圾收集器清除它.并且为了可重用性,会存在String在字符串池中,它很可能会保留在内存中持续很长时间,从而构成安全威胁。


  由于任何有权访问内存转储的人都可以以明文形式找到密码,这是另一个原因,你应该始终使用加密密码而不是纯文本。由于字符串是不可变的,所以不能更改字符串的内容,因为任何更改都会产生新的字符串,而如果你使用char[],你就可以将所有元素设置为空白或零。因此,在字符数组中存储密码可以明显降低窃取密码的安全风险。


  2)Java本身建议使用JPasswordField的getPassword()方法,该方法返回一个char[]和不推荐使用的getTex()方法,该方法以明文形式返回密码,由于安全原因。应遵循Java团队的建议,坚持标准而不是反对它。


  3)使用String时,总是存在在日志文件或控制台中打印纯文本的风险,但如果使用Array,则不会打印数组的内容而是打印其内存位置。虽然不是一个真正的原因,但仍然有道理。

image.png

  输出

image.png

  我还建议使用散列或加密的密码而不是纯文本,并在验证完成后立即从内存中清除它。因此,在Java中,用字符数组用存储密码比字符串是更好的选择。虽然仅使用char[]还不够,还你需要擦除内容才能更安全。


  这些是小编的核心Java面试问题和答案的清单。对于有经验的程序员来说,一些Java问题看起来并不那么难,但对于Java中的中级和初学者来说,它们真的很难回答。顺便说一句,如果你在面试中遇到任何棘手的Java问题,可以登录动力节点IT培训官网咨询在线客服老师。

提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>