专注Java教育14年 全国咨询/投诉热线:400-8080-105
动力节点LOGO图
始于2009,口口相传的Java黄埔军校
首页 学习攻略 Java学习 来自银行的10个Java面试题,仅供参考

来自银行的10个Java面试题,仅供参考

更新时间:2019-08-14 14:00:10 来源:动力节点 浏览6621次

Java常见面试题汇总.jpg

  问题一:在多线程环境中使用HashMap会有什么问题?在什么情况下使用get()方法会产生无限循环?

  HashMap本身没有什么问题,有没有问题取决于你是如何使用它的。比如,你在一个线程里初始化了一个HashMap然后在多个其他线程里对其进行读取,这肯定没有任何问题。有个例子就是使用HashMap来存储系统配置项。当有多于一个线程对HashMap进行修改操作的时候才会真正产生问题,比如增加、删除、更新键值对的时候。因为put()操作可以造成重新分配存储大小(re-sizeing)的动作,因此有可能造成无限循环的发生,所以这时需要使用Hashtable或者ConcurrentHashMap,而后者更优。

  问题二:不重写Bean的hashCode()方法是否会对性能带来影响?

  这个问题非常好,每个人可能都会有自己的体会。按照我掌握的知识来说,如果一个计算hash的方法写得不好,直接的影响是,当向HashMap中添加元素的时候会更频繁地造成冲突,因此最终增加了耗时。但是自从Java8开始,这种影响不再像前几个版本那样显著了,因为当冲突的发生超出了一定的限度之后,链表类的实现将会被替换成二叉树(binarytree)实现,这时你仍可以得到O(logN)的开销,优于链表类的O(n)。

  问题三:对于一个不可修改的类,它的每个对象是不是都必须声明成final的?

  不尽然,因为你可以通过将成员声明成非final且private,并且不要在除了构造函数的其他地方来修改它。不要为它们提供setter方法,同时不会通过任何函数泄露出对此成员的引用。需要记住的是,把对象声明成final仅仅保证了它不会被重新赋上另外一个值,你仍然可以通过此引用来修改引用对象的属性。这一点是关键,面试官通常喜欢听到你强调这一点。

  问题四:你在写存储过程或者在Java里调用存储过程的时候如何来处理错误情况?

  这是个很棘手的Java面试题,答案也并不固定。我的答案是,写存储过程的时候一旦有操作失败,则一定要返回错误码。但是在调用存储过程的时候出错的话捕捉SQLException却是唯一能做的。

  问题五:Executor.submit()和Executor.execute()这两个方法有什么区别?

  答案是:前者返回一个Future对象,可以通过这个对象来获得工作线程执行的结果。

  当我们考察异常处理的时候,又会发现另外一个不同。当你使用execute提交的任务抛出异常时,此异常将会交由未捕捉异常处理过程来处理(uncaughtexceptionhandler),当你没有显式指定一个异常处理器的话,默认情况下仅仅会通过System.err打印出错误堆栈。

  当你用submit来提交一个任务的时候,这个任务一旦抛出异常(无论是否是运行时异常),那这个异常是任务返回对象的一部分。对这样一种情形,当你调用Future.get()方法的时候,这个方法会重新抛出这个异常,并且会使用ExecutionException进行包装。

  问题六:什么是单例模式?创建单例对象的时候是将整个方法都标记为synchronized好还是仅仅把创建的的语句标记为synchronized好?

  在Java中,单例类是指那些在整个Java程序中只存在一份实例的类,例如java.lang.Runtime就是一个单例类。在Java4版本及以前创建单例会有些麻烦,但是自从Java5引入了Enum类型之后,事情就变得简单了。可以去看看我的关于如何使用Enum来创建单例类的文章,同时再看看问题五来看看如何在创建单例类的时候进行双重检查。

  问题七:能否写一段用Java4或5来遍历一个HashMap的代码?

  事实上,用Java可以有四种方式来遍历任何一个Map,一种是使用keySet()方法获取所有的键,然后遍历这些键,再依次通过get()方法来获取对应的值。第二种方法可以使用entrySet()来获取键值对的集合,然后使用foreach语句来遍历这个集合,遍历的时候获得的每个键值对已经包含了键和值。这种算是一种更优的方式,因为每轮遍历的时候同时获得了key和value,无需再调用get()方法,get()方法在那种如果bucket位置有一个巨大的链表的时候的性能开销是O(n)。第三种方法是获取entrySet之后用iterator依次获取每个键值对。第四种方法是获得keyset之后用iterator依次获取每个key,然后再根据key来调用get方法。

  问题八:你在什么时候会重写hashCode()和equals()方法?

  当你需要根据业务逻辑来进行相等性判断、而不是根据对象相等性来判断的时候你就需要重写这两个函数了。例如,两个Employee对象相等的依据是它们拥有相同的emp_id,尽管它们有可能是两个不同的Object对象,并且分别在不同的地方被创建。同时,如果你准备把它们当作HashMap中的key来使用的话,你也必须重写这两个方法。现在,作为Java中equals-hashcode的一个约定,当你重写equals的时候必须也重写hashcode,否则你会打破诸如Set,Map等集合赖以正常工作的约定。

  问题九:如果不重写hashCode方法会有什么问题?

  如果不重写equals方法的话,equals和hashCode之间的约定就会被打破:当通过equals方法返回相等的两个对象,他们的hashCode也必须一样。如果不重写hashCode方法的话,即使是使用equals方法返回值为true的两个对象,当它们插入同一个map的时候,因为hashCode返回不同所以仍然会被插入到两个不同的位置。这样就打破了HashMap的本来目的,因为Map本身不允许存进去两个key相同的值。当使用put方法插入一个的时候,HashMap会先计算对象的hashcode,然后根据它来找到存储位置(bucket),然后遍历此存储位置上所有的Map.Entry对象来查看是否与待插入对象相同。如果没有提供hashCode的话,这些就都做不到了。

  问题十:我们要同步整个getInstance()方法,还是只同步getInstance()方法中的关键部分?

  答案是:仅仅同步关键部分(CriticalSection)。这是因为,如果我们同步整个方法的话,每次有线程调用getInstance()方法的时候都会等待其他线程调用完成才行,即使在此方法中并没有执行对象的创建操作。换句话说,我们只需要同步那些创建对象的代码,而创建对象的代码只会执行一次。一旦对象创建完成之后,根本没有必要再对方法进行同步保护了。事实上,从性能上来说,对方法进行同步保护这种编码方法非常要命,因为它会使性能降低10到20倍。下面是单例模式的UML图。

image.png

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

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