Java面试问题集锦
1、HashMap和Hashtable的区别
HashMap和Hashtable的比较是Java面试中的常见问题,用来考验程序员是否能够正确使用集合类以及是否可以随机应变使用多种思路解 决问题。HashMap的工作原理、ArrayList与Vector的比较以及这个问题是有关Java 集合框架的最经典的问题。Hashtable是个过时的集合类,存在于Java API中很久了。在Java 4中被重写了,实现了Map接口,所以自此以后也成了Java集合框架中的一部分。Hashtable和HashMap在Java面试中相当容易被问到, 甚至成为了集合框架面试题中最常被考的问题,所以在参加任何Java面试之前,都不要忘了准备这一题。
这篇文章中,我们不仅将会看到HashMap和Hashtable的区别,还将看到它们之间的相似之处。
HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。
- HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。
- HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个 线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
- 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是 fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出 ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出 ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration 和Iterator的区别。
- 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
- HashMap不能保证随着时间的推移Map中的元素次序是不变的。
要注意的一些重要术语:
1) sychronized意味着在一次仅有一个线程能够更改Hashtable。就是说任何线程要更新Hashtable时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable。
2) Fail-safe和iterator迭代器相关。如果某个集合对象创建了Iterator或者ListIterator,然后其它的线程试图“结构上” 更改集合对象,将会抛出ConcurrentModificationException异常。但其它线程可以通过set()方法更改集合对象是允许的, 因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用set()方法,将会抛出IllegalArgumentException 异常。
3) 结构上的更改指的是删除或者插入一个元素,这样会影响到map的结构。
我们能否让HashMap同步?
HashMap可以通过下面的语句进行同步:
Map m = Collections.synchronizeMap(hashMap);
结论
Hashtable和HashMap有几个主要的不同:线程安全以及速度。仅在你需要完全的线程安全的时候使用Hashtable,而如果你使用Java 5或以上的话,请使用ConcurrentHashMap吧。
原文链接: Javarevisited 翻译: ImportNew.com - 唐小娟
译文链接: http://www.importnew.com/7010.html
Java异常的面试问题及答案
Java提供了一个健壮的、面向对象的方法来处理出现异常,称为Java异常处理。 我以前写过一篇长文章来介绍Java异常处理,今天我将列出一些重要的Java异常面试的问题及答案,希望对你们的面试有所帮助。
1.什么是Java异常
答:异常是发生在程序执行过程中阻碍程序正常执行的错误事件。比如:用户输入错误数据、硬件故障、网络阻塞等都会导致出现异常。 只要在Java语句执行中产生了异常,一个异常对象就会被创建,JRE就会试图寻找异常处理程序来处理异常。如果有合适的异常处理程序,异常对象就会被异 常处理程序接管,否则,将引发运行环境异常,JRE终止程序执行。 Java异常处理框架只能处理运行时错误,编译错误不在其考虑范围之内。
2.Java异常处理中有哪些关键字?
答:
- throw:有时我们需要显式地创建并抛出异常对象来终止程序的正常执行。throw关键字用来抛出并处理运行时异常。
- throws:当我们抛出任何“被检查的异常(checked exception)”并不处理时,需要在方法签名中使用关键字throws来告知调用程序此方法可能会抛出的异常。调用方法可能会处理这些异常,或者同 样用throws来将异常传给上一级调用方法。throws关键字后可接多个潜在异常,甚至是在main()中也可以使用throws。
- try-catch:我们在代码中用try-catch块处理异常。当然,一个try块之后可以有多个catch子句,try-catch块也能嵌套。每个catch块必须接受一个(且仅有一个)代表异常类型的参数。
- finally:finally块是可选的,并且只能配合try-catch一起使用。虽然异常终止了程序的执行,但是还有一些打开的资源没有被关闭,因此,我们能使用finally进行关闭。不管异常有没有出现,finally块总会被执行。
3.描述一下异常的层级。
答:Java异常是层级的,并通过继承来区分不同种类的异常。
- Throwable是所有异常的父类,它有两个直接子对象Error,Exception,其中Exception又被继续划分为“被检查的异常 (checked exception)”和”运行时的异常(runtime exception,即不受检查的异常)”。 Error表示编译时和系统错误,通常不能预期和恢复,比如硬件故障、JVM崩溃、内存不足等。
- 被检查的异常(Checked exception)在程序中能预期,并要尝试修复,如FileNotFoundException。我们必须捕获此类异常,并为用户提供有用信息和合适日志来进行调试。Exception是所有被检查的异常的父类。
- 运行时异常(Runtime Exception)又称为不受检查异常,源于糟糕的编程。比如我们检索数组元素之前必须确认数组的长度,否则就可能会抛出 ArrayIndexOutOfBoundException运行时异常。RuntimeException是所有运行时异常的父类。
答:Exception和它的所有子类没有提供任何特殊方法供使用,它们的所有方法都是来自其基类Throwable。
- String getMessage():方法返回Throwable的String型信息,当异常通过构造器创建后可用。
- String getLocalizedMessage():此方法通过被重写来得到用本地语言表示的异常信息返回给调用程序。Throwable类通常只是用getMessage()方法来实现返回异常信息。
- synchronized Throwable getCause():此方法返回异常产生的原因,如果不知道原因的话返回null。(原文有拼写错误 应该是if 不是id)
- String toString():方法返回String格式的Throwable信息,此信息包括Throwable的名字和本地化信息。
- void printStackTrace():该方法打印栈轨迹信息到标准错误流。该方法能接受PrintStream 和PrintWriter作为参数实现重载,这样就能实现打印栈轨迹到文件或流中。
5.描述Java 7 ARM(Automatic Resource Management,自动资源管理)特征和多个catch块的使用
答:如果一个try块中有多个异常要被捕获,catch块中的代码会变丑陋的同时还要用多余的代码来记录异常。有鉴于此,Java 7的一个新特征是:一个catch子句中可以捕获多个异常。示例代码如下:
catch(IOException | SQLException | Exception ex){ logger.error(ex); throw new MyException(ex.getMessage()); }
大多数情况下,当忘记关闭资源或因资源耗尽出现运行时异常时,我们只是用finally子句来关闭资源。这些异常很难调试,我们需要深入到资源使用 的每一步来确定是否已关闭。因此,Java 7用try-with-resources进行了改进:在try子句中能创建一个资源对象,当程序的执行完try-catch之后,运行环境自动关闭资 源。下面是这方面改进的示例代码:
try (MyResource mr = new MyResource()) { System.out.println("MyResource created in try-with-resources"); } catch (Exception e) { e.printStackTrace(); }
6.
被检查的异常和不受检查的异常有什么区别?
答:
A.
被检查的异常应该用
try-catch
块代码处理,或者在
main
方法中用
throws
关键字让
JRE
了解程序可能抛出哪些异常。不受检查的异常在程序中不要求被处理或用
throws
语句告知。
B.Exception
是所有被检查异常的基类,然而,
RuntimeException
是所有不受检查异常的基类。
C
.被检查的异常适用于那些不是因程序引起的错误情况,比如:读取文件时文件不存在引发的
FileNotFoundException
。然而,不被检查的异常通常都是由于糟糕的编程引起的,比如:在对象引用时没有确保对象非空而引起的
NullPointerException
。
7
.在
Java
中
throw
与
throws
关键字之间的区别?
答:
throws
用于在方法签名中声明此方法可能抛出的异常,而
throw
关键字则是中断程序的执行并移交异常对象到运行时进行处理。
8.
在
Java
中怎么写自定义的异常?
答:我们能继承
Exception
类或其任何子类来实现自己的自定义异常类。这自定义异常类可以有自己变量和方法来传递错误代码或其它异常相关信息来处理异常。
下面是一个简单的自定义异常示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package com.journaldev.exceptions; import java.io.IOException; public class MyException extends IOException { private static final long serialVersionUID = 4664456874499611218L; private String errorCode= "Unknown_Exception" ; public MyException(String message, String errorCode){ super (message); this .errorCode=errorCode; } public String getErrorCode(){ return this .errorCode; } } |
9.
在
Java
中什么是内存不足错误?
答:在
Java
中,
OutOfMemoryError
是 java.lang.VirtualMachineError
的一个子类,当堆内存耗尽时会被
JVM
抛出。我们能通过设置
Java
选项来提供更大的内存供应用使用来达到修复的目的。
1
|
$>java MyProgram -Xms1024m -Xmx1024m -XX:PermSize=64M -XX:MaxPermSize=256m |
10.
引发
Exception in thread main
的各种不同情形?
答:通常的一些主线程异常情形主要有以下几种:
Exception in thread main java.lang.UnsupportedClassVersionError:
当编译和运行
Java
类的
JDK
版本不同的时出现这种异常。
Exception in thread main java.lang.NoClassDefFoundError:
这种异常出现的原因有两种:第一种是提供类全名时附带有
.class
;第二种是指定类未找到。
Exception in thread main java.lang.NoSuchMethodError: main:
当试图运行一个没
main
方法的类时会出现这种异常。
Exception in thread main java.lang.NoSuchMethodError: main:
无论何时
main
方法任何异常,它打印异常到控制台。其第一部分是陈述
main
方法抛出的异常,第二部分打印异常类名,后接异常类信息。
想了解更多这方面的内容,请猛点这里。
11
.
Java
中
final,finally,finalize
的区别?
答:
final
和
finally
在
Java
中是关键字,而
finalize
则是一个方法。
final
关键字使得类变量不可变,避免类被其它类继承或方法被重写。
finally
跟
try-catch
块一起使用,即使是出现了异常,其子句总会被执行,通常,
finally
子句用来关闭相关资源。
finally
方法中的对象被销毁之前会被垃圾回收。
综上三者,只有
finally
用于异常处理。
12.
在
main
方法抛出异常时发生了什么?
答:当
main
方法抛出异常时,
Java
运行时间终止并在控制台打印异常信息和栈轨迹。
13.catch
子句能为空吗?
答:可以有空的
catch
子句,但那是最糟糕的编程,因为那样的话,异常即使被捕获,我们也得不到任何的有用信息,对于调试来说会是个噩梦,因此,编程时永远不要有空的
catch
子句。
Catch
子句中至少要包含一个日志语句输出到控制台或保存到日志文件中。
Java提供了一个健壮的、面向对象的方法来处理出现异常,称为Java异常处理。 今天我将列出一些重要的Java异常面试的问题及答案,希望对你们的面试有所帮助。这是这个主题的第三篇。第一篇请猛击<a href=”http://www.importnew.com/7383.html”>这里</a>,第二篇请猛击<a href=”http://www.importnew.com/7541.html”>这里</a>
14.提供一些Java异常处理的最佳实践。
答:有关Java异常处理的相关最佳实践如下:
- 使用具体的异常方便调试
- 程序中早点抛出异常
- 捕获异常后先让调用者处理异常
- 使用Java 7 ARM功能确保资源关闭或者用finally子句正确地关闭它们
- 为了调试需要总是记录异常信息
- 用多个catch子句实现更完全的关闭
- 你自己的应用API中用自定义的异常来抛出单种类型异常
- 遵循命名规定,以异常结束
- 在Javadoc中用@throws来标注方法抛出的异常
- 处理异常是有花销的,因此只有在必要时才抛出。否则,你会扑空或毫无收获。
15.下面的程序中有什么问题,该怎么去修改?
答:这里将针对一些跟异常相关的编程问题:
A.下面这段代码有什么问题呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.journaldev.exceptions; import java.io.FileNotFoundException; import java.io.IOException; public class TestException { public static void main(String[] args) { try { testExceptions(); } catch (FileNotFoundException | IOException e) { e.printStackTrace(); } } public static void testExceptions() throws IOException, FileNotFoundException { } } |
上面这段代码将不能被编译,并且会得到:The exception FileNotFoundException is already caught by the alternative IOException这样的错误信息,这是因为FileNotFoundException是IOException的子类。有两种方法来解决此问题: 第一种是用两个catch子句来处理这两个异常。代码如下:
1
2
3
4
5
6
7
|
try { testExceptions(); } catch (FileNotFoundException e){ e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } |
另一种方法就是在catch子句中移除FileNotFoundException,只用IOException。如:
1
2
3
4
5
|
try { testExceptions(); } catch (IOException e) { e.printStackTrace(); } |
你可以根据自己的catch子句情况选择上面的任一方法。
B.下面这段代码又有什么问题呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.journaldev.exceptions; import java.io.FileNotFoundException; import java.io.IOException; import javax.xml.bind.JAXBException; public class TestException1 { public static void main( String [] args) { try { go(); } catch (IOException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (JAXBException e) { e.printStackTrace(); } } public static void go() throws IOException, JAXBException, FileNotFoundException{ } } |
跟A代码一样,代码将不能编译,因为FileNotFoundException是IOException的子类,所 以,FileNotFoundException的catch子句将被隐藏,同时,你会得到这样的:Unreachable catch block for FileNotFoundException.的错误信息。因为异常已被IOException的catch子句处理。你需要改变catch子句的顺序来 修复程序。代码如下:
1
2
3
4
5
6
7
8
9
|
try { go(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (JAXBException e) { e.printStackTrace(); } |
JAXBException跟FileNotFoundException和IOException不相关,它能放在catch块层级的任何位置。
C.下面的代码同样存在问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package com.journaldev.exceptions; import java.io.IOException; import javax.xml.bind.JAXBException; public class TestException2 { public static void main(String[] args) { try { foo(); } catch (IOException e) { e.printStackTrace(); } catch (JAXBException e){ e.printStackTrace(); } catch (NullPointerException e){ e.printStackTrace(); } catch (Exception e){ e.printStackTrace(); } } public static void foo() throws IOException{ } } |
这段代码同样不能编译,因为JAXBException是个受检查的异常,而foo方法应该抛出此异常供调用方法捕获。你将会得 到:Unreachable catch block for JAXBException这样的错误信息。这个异常不可能从try子句中抛出。为解决这个错误,只能将JAXBException从catch子句中移 除。
也要注意到,NullPointerException的异常捕获是有效的,因为它是个不被检查的异常。
D.下面的代码存在什么问题呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
package com.journaldev.exceptions; public class TestException3 { public static void main(String[] args) { try { bar(); } catch (NullPointerException e){ e.printStackTrace(); } catch (Exception e){ e.printStackTrace(); } foo(); } public static void bar(){ } public static void foo() throws NullPointerException{ } } |
这代码是个幌子,根本没问题,能被正确编译。我们能捕获到一般异常或者是不被检查的异常,即使在throws语句中没被提及。
同样,如果程序中的一个方法foo()在throws中声明了不被检查的异常,程序中也不一定要处理这个异常。
E.下面这段代码同样存在瑕疵。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.journaldev.exceptions; import java.io.IOException; public class TestException4 { public void start() throws IOException{ } public void foo() throws NullPointerException{ } } class TestException5 extends TestException4{ public void start() throws Exception{ } public void foo() throws RuntimeException{ } } |
这段代码不能被编译,因为父类中start的方法签名与子类中的start方法签名不相同。为纠正这错误,我们可以修改子类的方法签名使之与超类相同,我们也可以像下面代码那样移除子类中throws关键字。
1
2
3
4
|
@Override public void start(){ } |
F.下面的代码存在什么问题呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package com.journaldev.exceptions; import java.io.IOException; import javax.xml.bind.JAXBException; public class TestException6 { public static void main(String[] args) { try { foo(); } catch (IOException | JAXBException e) { e = new Exception( "" ); e.printStackTrace(); } catch (Exception e){ e = new Exception( "" ); e.printStackTrace(); } } public static void foo() throws IOException, JAXBException{ } } |
这段代码同样不能编译,因为在多个catch子句中的异常对象是不可变的,我们不能改变其值。你会得到这样的:The parameter e of a multi-catch block cannot be assigned编译时错误信息。我们需要删掉将e赋值给新异常对象这句来修正错误。
想了解更多关于Java7多个catch子句的细节,请猛点这里。
这就是所有异常面试的问题,希望你们能喜欢。同时,我也希望将来能在列表中添加更多的问题,以适应未来的需要。
想了解更多的Java ARM的资讯,请猛点这里。
常见Java面试题 – 第一部分:非可变性(Immutability)和对象引用(Object reference)
一些比较核心的Java问题经常会用来考验面试者的Java基本知识功底。这篇文章列出来了一些在我的书里面没有的面试题,通过这些面试题读者也可以梳理一下Java基础知识点。
Q1.下面的代码片段会输出什么?
1
2
3
4
|
String s = " Hello " ; s += " World " ; s.trim( ); System.out.println(s); |
A1.正确输出是” Hello World “。
由于字符串前后都有空格,因为有些人可能会认为结果是”Hello World”。那么,这个题目想要考察的是什么呢?
1. 字符串对象(String Object)是非可变的(immutable),这个题目容易迷惑人的地方在s.trim( )这一行。
2. 理解对象引用和不可达对象会由垃圾回收器收集。
顺着这个题目,你觉得还可以考察哪些概念呢?
1. 例如,上面的代码中一共会生成几个字符串对象,什么时候这些对象会变成不可达对象从而被垃圾回收器回收。
2. 又比如,上面的代码的效率如何?
最好的解释方式是通过一个图表来说明,如下:
如果需要输出前后没有空格的”Hello World”,那么应该将s.trim( )再赋值给”s”。这个赋值操作可以让s指向新创建出来的字符串对象。
上面的代码也可以改写成如下方式:
1
2
3
|
StringBuilder sb = new StringBuilder( " Hello " ); sb.append( " World " ); System.out.println(sb.toString().trim( )); |
StringBuilder不是一个线程安全的类,因此仅仅用作本地变量是没有问题的。如果你希望用作实例变量,那么可以选择线程安全的StringBuffer类。想知道字符串操作背后的原理吗?可以点击这里:String concatenation。
扩展阅读:
- 常见Java面试题 – 第二部分:equals与==
- 常见Java面试题 – 第三部分:重载(overloading)与覆盖(overriding)
- 常见Java面试题 – 第四部分:迭代(iteration)和递归(recursion)
英文原文: Java Success,编译:ImportNew - 郑雯
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~