Java面试随着时间的流逝而改变。在过去,当你知道String和StringBuilder的区别(String类和StringBuilder类的主要区别在于String是不可变的对象。所以灭此对String对象进行改变的时候其实都等同于生成了一个新的String对象,而后将引用指向新的String对象,因此常常改变内容的字符串最好不要用String,由于每次生成String对象都会对系统性能产生影响,特别当内存中无引用对象多了之后,JVM的GC就会开始工做,影响性能,能够考虑使用可变字符序列StringBuilder),就能让你直接进入第二轮面试,可是如今问题变得愈来愈高级,面试官问的问题也更深刻。在我初入职的时候,相似于Vector与Array的区别、HashMap与Hashtable的区别是最流行的问题,只须要记住它们,就能在面试中得到更好的机会,但这种情形已经不复存在。现在,你将会被问到许多Java程序员都没有看过的领域,如NIO、设计模式、成熟的单元测试,或那些很难掌握的知识,如并发、算法、数据结构以及字符集编码解码。html
因为我喜欢研究面试题,所以我已经收集了许多的面试问题,包括许多不一样的主题。我已经为这众多的问题准备一段时间了,如今我将它们分享给大家。这里不但包含经典的面试问题,如线程、集合、equals和hashcode、socket,还包含了NIO、数组、字符串、Java 8等主题。java
该列表包含了入门级Java程序员和多年经验的高级开发者的问题。不管你是一、二、三、四、五、六、七、八、9仍是10年经验的开发者,你都能在其中找到一些有趣的问题。这里包含了一些超级容易回答的问题,也包含经验丰富的Java程序员也会棘手的问题。程序员
固然大家也是很是幸运的,当今有许多好书来帮助你准备Java面试,其中有一本我以为特别有用和有趣的是 Markham 的《Java程序面试揭秘》(Java Programming Interview Exposed)。这本书会告诉你一些Java和JEE面试中最重要的主题,即便你不是准备Java面试,也值得一读。面试
该问题列表特别长,咱们有各个方面的问题。因此,答案必须短小、简洁、干脆,不拖泥带水。所以,除了这一个段落,你只会听到问题与答案,再无其余内容,没有反馈,也没有评价。为此,我已经写好了一些博文,在这些文章中你能够找到我对某些问题的观点,如我为何喜欢这个问题,这个问题的挑战是什么?指望从面试者得到什么样的答案?正则表达式
这个列表有一点不一样。我鼓励你采用相似的方式去分享问题和答案,这样容易温习。我但愿这个列表对面试官和候选人都有很好的用处。面试官能够对这些问题作一些改变以获取新奇的元素,这对一次好的面试来讲很是重要。而候选者,能够扩展和测试Java程序语言和平台关键领域的知识。2015年,我会更多得关注并发、JVM内助,32位JVM和64位JVM的区别、单元测试以及简洁的代码。我确信,若是你读过这个庞大的Java面试问题列表,不管电话面试仍是面对面的面试,你都能有很好的表现。算法
Java面试的重要问题编程
除了你看到的惊人的问题数量,我也尽可能保证质量。我不止一次分享各个重要主题中的问题,也确保包含所谓的高级话题。这些话题不少程序员不喜欢准备或直接放弃,由于他们的工做不会涉及到这些。Java NIO和JVM底层就是最好的例子。你也能够将设计模式划分到这一类中,可是愈来愈多有经验的程序员了解GoF设计模式并应用这些模式。我也尽可能在这个列表中包含2015年最新的面试问题。这些问题多是来年关注的核心。为了给你一个大体的了解,下面列出这份Java面试问题列表包含的主题:设计模式
120大Java面试题及答案数组
如今是时候给你展现我近5年从各类面试中收集来的120个问题了。我肯定你在本身的面试中见过不少这些问题,不少问题你也能正确回答。缓存
多线程、并发及线程的基础问题
1)Java中能建立 volatile 数组吗?
能,Java中能够建立volatile类型数组,不过只是一个指向数组的引用,而不是整个数组。个人意思是,若是改变引用指向的数组,将会受到volatile的保护,可是若是多个线程同时改变数组的元素,volatile标识符就不能起到以前的保护做用了。
2)volatile 能使得一个非原子操做变成原子操做吗?
一个典型的例子是在类中有一个long类型的成员变量,若是你知道该成员变量会被多个线程访问,如计数器、价格等,你最好将其设置为volatile。为何?由于Java中读取long类型变量不是原子的,须要分红两步,若是一个线程正在修改该long变量的值,另外一个线程可能看到该值得一半(前32位)。可是对一个 volatile 型的 long 或 double 变量的读写是原子操做。
3)对 volatile 修饰符有过什么实践?
一种实践是用 volatile 修饰 long 和 double 变量,使其能按原子类型来读写。double 和 long 都是64位宽,所以对这两种类型的读是分为两部分的,第一次读取前32位,而后再读取剩下的32位,这个过程不是原子的,但 Java 中 volatile 修饰的 long 或 double 变量的读写是原子的。volatile 修饰符的另外一个做用是提供内存屏障(memory barrier),例如在分布式框架中的应用。简单地说,就是当你写一个 volatile 变量以前,Java 内存模型会插入一个写屏障(write barrier),读一个 volatile 变量以前,会插入一个读屏障(read barrier)。意思是说,在你写一个 volatile 域时,能保证任何线程都能看到你写的值;同时,在写以前,也能保证任何数值的更新对全部线程是可见的,由于内存屏障会将其余全部写的值更新到缓存。
4)volatile 修饰的变量提供什么保证?
volatile 变量提供顺序和可见性保证。例如,JVM 或 JIT 为了得到更好的性能会对语句重排序,但 volatile 修饰的变量即便在没有同步块的状况下赋值也不会与其余语句重排序。volatile 提供 happens-before 的保证,确保一个线程的修改能对其余线程是可见的。某些状况下,volatile 还能提供原子性,如读 64 位数据类型,像 long 和 double 都不是原子的,但 volatile 修饰的 double 和 long 就是原子的。
5)10 个线程和 2 个线程的同步代码,哪一个更容易些?
从写代码的角度来讲,二者的复杂度是相同的,由于同步代码与线程数量是相互独立的。可是同步策略的选择依赖于线程的数量,由于越多的线程意味着更大的竞争,因此你须要利用同步技术,如锁分离,这要求更复杂的代码和专业知识。
6)你是如何调用 wait() 方法的?使用 if 块仍是循环?为何?
wait() 方法应该在循环中调用,由于当线程获取到 CPU 开始执行的时候,其余条件可能尚未知足,因此在处理前,循环检测条件是否知足会更好。下面是一段标准的使用 wait 和 notify 方法的代码:
// The standard idiom for using the wait method synchronized(obj){ while(condition does not hold) obj.wait(); // (Release lock, and reacquires on wakeup) ... // Perform action appropriate to condition }
参见 《Effective Java》第 69 条,获取更多关于为何应该在循环中调用 wait 方法的内容。
7)什么是多线程环境下的伪共享(false sharing)?
伪共享是多线程系统(每一个处理器有本身的局部缓存)中一个众所周知的性能问题。伪共享发生在不一样处理器上的线程对变量的修改依赖于相同的缓存行,以下图所示:
伪共享问题很难被发现,由于线程可能访问彻底不一样的全局变量,内存中却碰巧在很相近的位置上。如其余诸多的并发问题,避免伪共享的最基本方式是仔细审查代码,根据缓存行来调整你的数据结构。关于伪共享的扩展阅读
有经验程序员的 Java 面试题
8)什么是 Busy spin?咱们为何要使用它?
Busy spin 是一种在不释放 CPU 的基础上等待事件的技术。它常常用于避免丢失 CPU 缓存中的数据(若是线程先暂停,以后在其余CPU上运行就会丢失)。因此,若是你的工做要求低延迟,而且你的线程目前没有任何顺序,这样你就能够经过循环检测队列中的新消息来代替调用 sleeep() 或 wait() 方法。它惟一的好处就是你只需等待很短的时间,如几微秒或几纳秒。LMAX 分布式框架是一个高性能线程间通讯的库,该库有一个 BusySpinWaitStrategy 类就是基于这个概念实现的,使用 busy spin 循环 EventProcessors 等待屏障。关于Busy spin的扩展阅读 Disruptor入门 Java 核心
9)Java 中怎么获取一份线程 dump 文件?
在 Linux 下,你能够经过命令 kill -3 PID (Java 的进程D)来获取 Java 应用的 dump 文件。在 Windows 下,你能够按下 Ctrl + Break 来获取。这样 JVM 就会将线程的 dump 文件打印到标准输出或错误文件中,它可能打印在控制台或日志文件中,具体位置依赖应用的配置。假设你使用Tomcat。
10)Swing 是线程安全的吗?
不是,Swing 不是线程安全的。你不能经过任何线程来更新 Swing 组件,如 JTable、JList 或 JPanel。事实上,它们只能经过 GUI 或 AWT 线程来更新。这就是为何 Swing 提供 invokeAndWait() 和 invokeLater() 方法来获取其余线程的 GUI 更新请求。这些方法将更新请求放入 AWT 的线程队列中,能够一直等待,也能够经过异步更新直接返回结果。你也能够在参考答案中查看和学习到更详细的内容。
11)什么是线程局部变量?
当使用 ThreadLocal 维护变量时,ThreadLocal