原文地址: https://dzone.com/articles/10...
有为数不少的开发者希望能在像 Barclays、Credit Suisse、Citibank 等等那样的投资银行做 Java 开发工作,但是他们当中很多不知道自己会遇到什么样的面试题。
本文介绍一些来自投资银行的、针对三年以上经验的 Java 开发人员面试题。之所以不针对两年经验或以下的新手程序员,是因为投资银行通常会从毕业生那里去招这种水平的人,而不是通过社招。
也不是说这些问题一定会问到,只是给你一个印象,实际面试的时候会以哪些方面的题目为主。
当然,准备做得越充足就越好,所以如果这十道题对你来讲还不够,那么可以再看看电话面试四十题。这里还有一个两百多道题的大餐(译注:这是在线课程广告链接。后面的广告链接我都将去除)等着你。
◉ 第一题:多线程环境中使用 HashMap 会出现什么问题?什么情况下 get() 方法会陷入无限循环?(答案)
怎么说呢,不管什么东西,只要用对了就不会有问题。比如说一个 HashMap,你只用一个线程初始化,然后所有的线程都对它只读,那是完全没问题的。
一个典型例子就是包含配置属性的 Map 对象。如果有线程开始去写这个 Map,去添加、修改或删除里面的键值对,哪怕只有一个线程,问题就来了。
put() 方法可能会触发它内部的容量大小变更(re-sizing),这个过程可能会造成无限循环,这就是为什么你应该换用 Hashtable 或 ConcurrentHashMap(后者更佳)。
译注:关于 HashMap 陷入无限循环的可能,请参考这篇文章。
◉ 第二题:覆写 hashCode() 方法会不会对性能有影响?(答案)
这个问题很棒,也很开放。据我所知,hashCode() 方法实现的不好,可能会导致 HashMap 频繁出现冲突,增加了将对象放入 HashMap 的耗时。
不过从 Java 8 开始这种情况有所改善,因为冲突到达一定级别后,HashMap 会改用二叉树而不是链表来保存内容,这样时间复杂度就从 O(N) 降低到了 O(log N)。
这个问题属于比较刁钻的面试题之一,很多开发者只知道 hashCode() 和 equals() 之间的关系,不会想到性能方面可能出现的问题。
◉ 第三题:不可变对象的所有属性都必须声明为 final 吗?(答案)
不是必须的。成员可以声明为 private 并且只在构造函数里面赋值,能达到同样的效果。
关键是不要提供 setter 方法。如果该属性是可变对象,还注意不要把它的引用泄露出去。
记住,将一个引用变量声明为 final 仅表示不允许对该变量重新赋值,你仍然可以修改该变量值自身的属性。如果求职者能够把问题阐明到这个层次,在面试官面前还能多拿点印象分哦。
◉ 第四题:String 的 substring() 方法的原理是什么?(答案)
很多开发人员会说:“这个方法就是从原始字符串中取出一部分,作为新的 String 对象返回。”
这样是没回答到点子上的,因为这个问题实际上是在考察你是否熟悉 substring() 方法造成内存泄漏的风险。
在 Java 1.7 之前,substring() 方法的返回值会持有原始字符串的引用,导致原始字符串无法被回收,也就是说,哪怕 substring() 返回的字符串只有 5 个字节,也会令原始大小 1GB 的字符串无法回收。
从 Java 1.7 开始这个问题被修复了,substring() 方法的返回值不再引用原始字符串,不过相应的代价是执行时间有所增加,时间复杂度从之前的 O(1) 增加到最坏情形下的 O(N)。
◉ 第五题:你能写出单例模式的关键代码吗?(答案)
本题属于 Java 核心题目,跟上一题有一脉相承的关系。提问者会期待面试者写出包含 双重检查锁定模式 的代码。
所以一定要记得在单例模式中使用 volatile 变量。
下面是一个使用双重检查锁定模式来实现单例模式的示例:
public class Singleton {
private static volatile Singleton _instance;
/**
* Double checked locking code on Singleton
* @return Singelton instance
*/
public static Singleton getInstance() {
if (_instance == null) {
synchronized(Singleton.class) {
if (_instance == null) {
_instance = new Singleton();
}
}
}
return _instance;
}
}
◉ 第六题:在编写存储过程或者用 Java 调用存储过程时,如何处理错误条件?(答案)
这个问题很开放,也是 Java 面试难题 之一。我是从一个朋友那里听到这个问题的,他也回答不出。
我的解决方案是,存储过程当中如果某些操作失败,那么应该返回一个错误代码;但要是存储过程本身调用失败,那就只能捕获 SQLException 了。
◉ 第七题:Executor.submit() 和 Executer.execute() 这两个方法有什么不同?(答案)
本题取自我的 “投资银行针对有经验的开发者的 五十个 Java 多线程面试题 列表”。这类问题越来越流行,因为企业对熟练掌握多线程开发的程序员的需求越来越多。如果你想认真打造自己的并发编程技能,我这里顺便推荐一下 Heinz Kabutz 的 Java 多线程实战课程。
回到本题,前者返回的是一个 Future 对象,它用来从 Worker 线程中获取处理结果。(译注:后者即 execute() 方法没有返回值)
还有一个区别就是对异常的处理。当 execute() 执行的任务发生异常时,该异常会进入未捕获异常处理流程(如果你没有提供自己的处理方式,则会从 System.err 将异常信息打印出来)。
当 submit() 执行的任务发生异常时,该异常会成为返回值的一部分。如果任务因为异常而结束运行,那么当调用 Future.get() 方法获取返回值时,这个异常会被包装在 ExecutionException 中再次抛出。
◉ 第八题:工厂(Factory)模式和抽象工厂(Abstract Factory)模式有什么区别?(答案)
抽象工厂模式比工厂模式多了一层抽象。
一个抽象工厂可以有多个不同的实现,它们各自负责创建不同种类的实际对象。如下图所示:
◉ 第九题:什么是单例?是用 synchronized 修饰整个方法更好,还是在方法里面用同步块更好?(答案)
单例就是指某个类在整个 Java 应用当中只有一个实例对象。比如 java.lang.Runtime 就是一个单例的类。在 Java 5 之前,设计单例类要非常小心容易出错,但自从 Java 5 引进了枚举之后,事情变得轻松多了。
请参考本人的文章 如何编写线程安全的单例,该文详细介绍了如何使用枚举或双重锁定检查模式来实现单例,这也是本面试题的目的所在。
◉ 第十题:在 Java 4 和 Java 5 中如何遍历 HashMap?(答案)
这个题目也比较刁钻,上面的答案给出了使用 while 和 for 循环来进行遍历的例子。实际上,在 Java 中遍历 Map 有四种方法。(译注:分别是 for-keyset、while-keyset、for-entryset 和 while-entryset)
遍历 keySet() 然后对每个 key 调用 get() 方法来取值,这种方式相对效率较低。
另一种方式就是遍历 entrySet(),然后直接从 Entry 中取 key 或 value。这种方式效率较高,因为你无需再调用 get() 方法取值。如果该方法遇到了较长的链表(译注:参考第二题),时间复杂度将会变成 O(N),虽然在 Java 8 中情况会稍微好点,因为 Java 8 会用二叉树代替链表。
总结
以上就是投资银行通常会出的 Java 面试题。如果你想在当中谋个 Java 开发者职位,那么请做好面对大量有关 Java 多线程、并发、集合、JVM 内部实现、垃圾收集,以及 Java 应用性能调优的各种问题。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。