挑战10个最难的Java面试题(附答案)【上】

这是收集的10个最棘手的Java面试问题列表。这些问题主要来自 Java 核心部分 ,不涉及 Java EE 相关问题。你可能知道这些棘手的 Java 问题的答案,或者以为这些不足以挑战你的 Java 知识,但这些问题都是容易在各类 Java 面试中被问到的,并且包括个人朋友和同事在内的许多程序员都以为很难回答。javascript

1 为何等待和通知是在 Object 类而不是 Thread 中声明的?java

一个棘手的 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 问题,由于这个继承方案的结构相似于菱形,见下图:

A foo()    
               /     
             /         
 foo() B     C foo()    
                  /    
                /    
               D  foo()

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

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

为何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++。

为何 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的 VVS Laxman,即很是特殊的类。我尚未看到一个没有使用 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 类的惟一设计者能够放心地回答它。我在 Joshua Bloch 的 Effective Java 书中期待一些线索,但他也没有提到它。我认为如下几点解释了为何 String 类在 Java 中是不可变的或 final 的:

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

String A="Test";
String B="Test";

如今字符串 B 调用 "Test".toUpperCase(), 将同一个对象改成“TEST”,因此 A 也是 “TEST”,这不是指望的结果。

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

2)字符串已被普遍用做许多 Java 类的参数,例如,为了打开网络链接,你能够将主机名和端口号做为字符串传递,你能够将数据库 URL 做为字符串传递, 以打开数据库链接,你能够经过将文件名做为参数传递给 File I/O 类来打开 Java 中的任何文件。若是 String 不是不可变的,这将致使严重的安全威胁,个人意思是有人能够访问他有权受权的任何文件,而后能够故意或意外地更改文件名并得到对该文件的访问权限。因为不变性,你无需担忧这种威胁。这个缘由也说明了,为何 String 在 Java 中是最终的,经过使 java.lang.String final,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中是最终的”。要想是不可变的,你必须是最终的,这样你的子类不会破坏不变性。你怎么看?

为何 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,则不会打印数组的内容而是打印其内存位置。虽然不是一个真正的缘由,但仍然有道理。

String strPassword =“Unknown”; 
    char [] charPassword = new char [] {'U','n','k','w','o','n'}; 
    System.out.println(“字符密码:”+ strPassword);
    System.out.println(“字符密码:”+ charPassword);

输出

字符串密码:Unknown
字符密码:[C @110b053

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

6 如何使用双重检查锁定在 Java 中建立线程安全的单例?

这个 Java 问题也常被问: 什么是线程安全的单例,你怎么建立它。好吧,在Java 5以前的版本, 使用双重检查锁定建立单例 Singleton 时,若是多个线程试图同时建立 Singleton 实例,则可能有多个 Singleton 实例被建立。从 Java 5 开始,使用 Enum 建立线程安全的Singleton很容易。但若是面试官坚持双重检查锁定,那么你必须为他们编写代码。记得使用volatile变量。

为何枚举单例在 Java 中更好

枚举单例是使用一个实例在 Java 中实现单例模式的新方法。虽然Java中的单例模式存在很长时间,但枚举单例是相对较新的概念,在引入Enum做为关键字和功能以后,从Java5开始在实践中。本文与以前关于 Singleton 的内容有些相关, 其中讨论了有关 Singleton 模式的面试中的常见问题, 以及 10 个 Java 枚举示例, 其中咱们看到了如何通用枚举能够。这篇文章是关于为何咱们应该使用Eeame做为Java中的单例,它比传统的单例方法相比有什么好处等等。

Java 枚举和单例模式

Java 中的枚举单例模式是使用枚举在 Java 中实现单例模式。单例模式在 Java 中早有应用, 但使用枚举类型建立单例模式时间却不长. 若是感兴趣, 你能够了解下构建者设计模式和装饰器设计模式。

1) 枚举单例易于书写

这是迄今为止最大的优点,若是你在Java 5以前一直在编写单例, 你知道, 即便双检查锁定, 你仍能够有多个实例。虽然这个问题经过 Java 内存模型的改进已经解决了, 从 Java 5 开始的 volatile 类型变量提供了保证, 可是对于许多初学者来讲, 编写起来仍然很棘手。与同步双检查锁定相比,枚举单例实在是太简单了。若是你不相信, 那就比较一下下面的传统双检查锁定单例和枚举单例的代码:

在 Java 中使用枚举的单例

这是咱们一般声明枚举的单例的方式,它可能包含实例变量和实例方法,但为了简单起见,我没有使用任何实例方法,只是要注意,若是你使用的实例方法且该方法能改变对象的状态的话, 则须要确保该方法的线程安全。默认状况下,建立枚举实例是线程安全的,但 Enum 上的任何其余方法是否线程安全都是程序员的责任

/**
* 使用 Java 枚举的单例模式示例
*/
public enum EasySingleton{
    INSTANCE;
}

你能够经过EasySingleton.INSTANCE来处理它,这比在单例上调用getInstance()方法容易得多。

具备双检查锁定的单例示例

下面的代码是单例模式中双重检查锁定的示例,此处的 getInstance() 方法检查两次,以查看 INSTANCE 是否为空,这就是为何它被称为双检查锁定模式,请记住,双检查锁定是代理以前Java 5,但Java5内存模型中易失变量的干扰,它应该工做完美。

/**
* 单例模式示例,双重锁定检查
*/
public class DoubleCheckedLockingSingleton{
     private volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

你能够调用DoubleCheckedLockingSingleton.getInstance() 来获取此单例类的访问权限。

如今,只需查看建立延迟加载的线程安全的 Singleton 所需的代码量。使用枚举单例模式, 你能够在一行中具备该模式, 由于建立枚举实例是线程安全的, 而且由 JVM 进行。

人们可能会争辩说,有更好的方法来编写 Singleton 而不是双检查锁定方法, 但每种方法都有本身的优势和缺点, 就像我最喜欢在类加载时建立的静态字段 Singleton, 以下面所示, 但请记住, 这不是一个延迟加载单例:

单例模式用静态工厂方法

这是我最喜欢的在 Java 中影响 Singleton 模式的方法之一,由于 Singleton 实例是静态的,而且最后一个变量在类首次加载到内存时初始化,所以实例的建立本质上是线程安全的。

/**
* 单例模式示例与静态工厂方法
*/
public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}

你能够调用 Singleton.getSingleton() 来获取此类的访问权限。

2) 枚举单例自行处理序列化

传统单例的另外一个问题是,一旦实现可序列化接口,它们就再也不是 Singleton, 由于 readObject() 方法老是返回一个新实例, 就像 Java 中的构造函数同样。经过使用 readResolve() 方法, 经过在如下示例中替换 Singeton 来避免这种状况:

//readResolve to prevent another instance of Singleton
private Object readResolve(){
    return INSTANCE;
}

若是 Singleton 类保持内部状态, 这将变得更加复杂, 由于你须要标记为 transient(不被序列化),但使用枚举单例, 序列化由 JVM 进行。

3) 建立枚举实例是线程安全的

如第 1 点所述,由于 Enum 实例的建立在默认状况下是线程安全的, 你无需担忧是否要作双重检查锁定。

总之, 在保证序列化和线程安全的状况下,使用两行代码枚举单例模式是在 Java 5 之后的世界中建立 Singleton 的最佳方式。你仍然可使用其余流行的方法, 如你以为更好, 欢迎讨论。

7 编写 Java 程序时, 如何在 Java 中建立死锁并修复它?

经典但核心Java面试问题之一。

若是你没有参与过多线程并发 Java 应用程序的编码,你可能会失败。

如何避免 Java 线程死锁?

如何避免 Java 中的死锁?是 Java 面试的热门问题之一, 也是多线程的编程中的重口味之一, 主要在招高级程序员时容易被问到, 且有不少后续问题。尽管问题看起来很是基本, 但大多数 Java 开发人员一旦你开始深刻, 就会陷入困境。

面试问题老是以“什么是死锁?”开始 

当两个或多个线程在等待彼此释放所需的资源(锁定)并陷入无限等待便是死锁。它仅在多任务或多线程的状况下发生。

如何检测 Java 中的死锁?

虽然这能够有不少答案, 但个人版本是首先我会看看代码, 若是我看到一个嵌套的同步块,或从一个同步的方法调用其余同步方法, 或试图在不一样的对象上获取锁, 若是开发人员不是很是当心,就很容易形成死锁。

另外一种方法是在运行应用程序时实际锁定时找到它, 尝试采起线程转储,在 Linux 中,你能够经过kill -3命令执行此操做, 这将打印应用程序日志文件中全部线程的状态, 而且你能够看到哪一个线程被锁定在哪一个线程对象上。

你可使用 fastthread.io 网站等工具分析该线程转储, 这些工具容许你上载线程转储并对其进行分析。

另外一种方法是使用 jConsole 或 VisualVM, 它将显示哪些线程被锁定以及哪些对象被锁定。

若是你有兴趣了解故障排除工具和分析线程转储的过程, 我建议你看看 Uriah Levy 在多元视觉(PluraIsight)上《分析 Java 线程转储》课程。旨在详细了解 Java 线程转储, 并熟悉其余流行的高级故障排除工具。

编写一个将致使死锁的Java程序?

一旦你回答了前面的问题,他们可能会要求你编写代码,这将致使Java死锁。

这是个人版本之一

/**
 * Java 程序经过强制循环等待来建立死锁。
 *
 *
 */
public class DeadLockDemo {

    /*
     * 此方法请求两个锁,第一个字符串,而后整数
     */
     public void method1() {
        synchronized (String.class) {
            System.out.println("Aquired lock on String.class object");

            synchronized (Integer.class) {
                System.out.println("Aquired lock on Integer.class object");
            }
        }
    }


    /*
     * 此方法也请求相同的两个锁,但彻底
     * 相反的顺序,即首先整数,而后字符串。
     * 若是一个线程持有字符串锁,则这会产生潜在的死锁
     * 和其余持有整数锁,他们等待对方,永远。
     */
     public void method2() {
        synchronized (Integer.class) {
            System.out.println("Aquired lock on Integer.class object");

            synchronized (String.class) {
                System.out.println("Aquired lock on String.class object");
            }
        }
    }
}

若是 method1() 和 method2() 都由两个或多个线程调用,则存在死锁的可能性, 由于若是线程 1 在执行 method1() 时在 Sting 对象上获取锁, 线程 2 在执行 method2() 时在 Integer 对象上获取锁, 等待彼此释放 Integer 和 String 上的锁以继续进行一步, 但这永远不会发生。

此图精确演示了咱们的程序, 其中一个线程在一个对象上持有锁, 并等待其余线程持有的其余对象锁。

你能够看到, Thread1 须要 Thread2 持有的 Object2 上的锁,而 Thread2 但愿得到 Thread1 持有的 Object1 上的锁。因为没有线程愿意放弃, 所以存在死锁, Java 程序被卡住。

其理念是, 你应该知道使用常见并发模式的正确方法, 若是你不熟悉这些模式,那么 Jose Paumard 《应用于并发和多线程的常见 Java 模式》是学习的好起点。

如何避免Java中的死锁?

如今面试官来到最后一部分, 在我看来, 最重要的部分之一; 如何修复代码中的死锁?或如何避免Java中的死锁?

若是你仔细查看了上面的代码,那么你可能已经发现死锁的真正缘由不是多个线程, 而是它们请求锁的方式, 若是你提供有序访问, 则问题将获得解决。

下面是个人修复版本,它经过避免循环等待,而避免死锁, 而不须要抢占, 这是须要死锁的四个条件之一。

public class DeadLockFixed {

    /**
     * 两种方法如今都以相同的顺序请求锁,首先采用整数,而后是 String。
     * 你也能够作反向,例如,第一个字符串,而后整数,
     * 只要两种方法都请求锁定,二者都能解决问题
     * 顺序一致。
     */
    public void method1() {
        synchronized (Integer.class) {
            System.out.println("Aquired lock on Integer.class object");

            synchronized (String.class) {
                System.out.println("Aquired lock on String.class object");
            }
        }
    }

    public void method2() {
        synchronized (Integer.class) {
            System.out.println("Aquired lock on Integer.class object");

            synchronized (String.class) {
                System.out.println("Aquired lock on String.class object");
            }
        }
    }
}

如今没有任何死锁,由于两种方法都按相同的顺序访问 Integer 和 String 类文本上的锁。所以,若是线程 A 在 Integer 对象上获取锁, 则线程 B 不会继续, 直到线程 A 释放 Integer 锁, 即便线程 B 持有 String 锁, 线程 A 也不会被阻止, 由于如今线程 B 不会指望线程 A 释放 Integer 锁以继续。

8 若是你的Serializable类包含一个不可序列化的成员,会发生什么?你是如何解决的?

任何序列化该类的尝试都会因NotSerializableException而失败,但这能够经过在 Java中 为 static 设置瞬态(trancient)变量来轻松解决。

Java 序列化相关的常见问题 

Java 序列化是一个重要概念, 但它不多用做持久性解决方案, 开发人员大多忽略了 Java 序列化 API。根据个人经验, Java 序列化在任何 Java核心内容面试中都是一个至关重要的话题, 在几乎全部的网面试中, 我都遇到过一两个 Java 序列化问题, 我看过一次面试, 在问几个关于序列化的问题以后候选人开始感到不自在, 由于缺少这方面的经验。

他们不知道如何在 Java 中序列化对象, 或者他们不熟悉任何 Java 示例来解释序列化, 忘记了诸如序列化在 Java 中如何工做, 什么是标记接口, 标记接口的目的是什么, 瞬态变量和可变变量之间的差别, 可序列化接口具备多少种方法, 在 Java 中,Serializable 和 Externalizable 有什么区别, 或者在引入注解以后, 为何不用 @Serializable 注解或替换 Serializalbe 接口。

查看挑战10个最难的Java面试题(附答案)【下】

做者:程序员私房菜

 

往期文章精选

若是让你手写个栈和队列,你还会写吗?

 周杰伦新歌《说好不哭》上线,程序员哭了......

javascript基础修炼(13)——记一道有趣的JS脑洞练习题

【个人物联网成长记3】如何开发物联网应用?

【HC资料合集】2019华为全联接大会主题资料一站式汇总,免费下载!