Java提供了一种健壮且面向对象的处理异常情况的方法,称为Java异常处理。之前我写了一篇关于Java异常处理的长篇文章,今天我列出了一些重要的Java异常问题及答案,以帮助你面试。
- 什么是Java中的异常?
- Java中的异常处理关键字有哪些?
- 解释Java异常层次结构?
- Java异常类的重要方法有哪些?
- 解释Java 7中的ARM特性和多捕获块?
- Java中检查异常和非检查异常的区别是什么?
- Java中throw和throws关键字的区别是什么?
- 如何在Java中编写自定义异常?
- Java中的OutOfMemoryError是什么?
- 导致“主线程中的异常”发生的不同情况有哪些?
- Java中final、finally和finalize的区别是什么?
- 当主方法抛出异常时会发生什么?
- 我们可以有一个空的 catch 块吗?
- 提供一些 Java 异常处理最佳实践?
- 以下程序有什么问题,我们如何解决?
1. Java 中的异常是什么?
异常是程序执行过程中可能发生的错误事件,打断其正常流程。异常可以由不同种类的情况引起,如用户输入错误的数据,硬件故障,网络连接失败等。每当在执行 Java 语句时发生任何错误,都会创建一个异常对象,然后 JRE 试图找到异常处理程序来处理异常。如果找到合适的异常处理程序,则将异常对象传递给处理程序代码来处理异常,称为 捕获异常。如果找不到处理程序,则应用程序将异常抛给运行时环境,JRE 终止程序。 Java 异常处理 框架用于处理运行时错误,编译时错误不由异常处理框架处理。
2. Java 中的异常处理关键字是什么?
Java 异常处理中使用了四个关键字。
- throw:有时我们希望明确创建一个异常对象,然后将其抛出以停止程序的正常处理。使用 throw 关键字将异常抛给运行时来处理。
- throws:当我们在方法中抛出任何已检查异常而不加以处理时,就需要在方法签名中使用 throws 关键字,让调用程序知道方法可能抛出的异常。调用方法可能会处理这些异常,也可以使用
throws
关键字将其传播给其调用方法。我们可以在 throws 子句中提供多个异常,并且它也可以与 main() 方法一起使用。 - try-catch:我们在代码中使用 try-catch 块进行异常处理。try 是块的起始部分,catch 在 try 块的末尾用于处理异常。我们可以在 try 块中有多个 catch 块,并且 try-catch 块也可以嵌套。catch 块需要一个参数,该参数应为 Exception 类型。
- 最終:finally塊是可選的,僅與try-catch塊一起使用。由於異常會停止執行過程,我們可能會有一些未關閉的資源,因此我們可以使用finally塊。finally塊始終被執行,無論是否發生異常。
3. 解釋Java異常層次結構?
Java異常是層次化的,並且使用繼承來分類不同類型的異常。 Throwable是Java異常層次結構的父類,它有兩個子對象 – Error和Exception。異常進一步分為已檢查異常和運行時異常。錯誤是超出應用範圍的異常情況,我們無法預測並從中恢復,例如硬件故障、JVM崩潰或內存不足錯誤。已檢查的異常是我們可以在程序中預期並嘗試從中恢復的異常情況,例如FileNotFoundException。我們應該捕獲此異常並向用戶提供有用的消息,並適當地記錄以供調試。Exception是所有已檢查異常的父類。運行時異常是由編程錯誤引起的,例如嘗試從數組中檢索元素。我們應該先檢查數組的長度,然後再嘗試檢索元素,否則它可能會在運行時拋出ArrayIndexOutOfBoundException。RuntimeException是所有運行時異常的父類。
4. Java Exception 类的重要方法有哪些?
异常类及其所有子类没有提供任何特定的方法,所有方法都是在基类 Throwable 中定义的。
- String getMessage() – 此方法返回 Throwable 的消息字符串,消息可以在创建异常时通过其构造函数提供。
- String getLocalizedMessage() – 此方法提供给子类重写,以向调用程序提供特定于区域设置的消息。Throwable 类的此方法的实现仅使用
getMessage()
方法返回异常消息。 - synchronized Throwable getCause() – 此方法返回异常的原因,如果原因未知,则返回 null。
- String toString() – 此方法以字符串格式返回 Throwable 的信息,返回的字符串包含 Throwable 类的名称和本地化消息。
- void printStackTrace() – 此方法将堆栈跟踪信息打印到标准错误流,此方法已重载,我们可以将 PrintStream 或 PrintWriter 作为参数传递以将堆栈跟踪信息写入文件或流。
5. 解釋 Java 7 ARM 功能和多重捕獲區塊?
如果你在一個單一的 try 區塊中捕獲了很多異常,你會注意到捕獲區塊的代碼看起來非常雜亂,並且主要由重複的代碼組成,用於記錄錯誤。著眼於此,Java 7 的一個功能是多重捕獲區塊,我們可以在單個捕獲區塊中捕獲多個異常。具有此功能的捕獲區塊如下所示:
catch(IOException | SQLException | Exception ex){
logger.error(ex);
throw new MyException(ex.getMessage());
}
大多數情況下,我們使用 finally 區塊僅僅是為了關閉資源,有時我們會忘記關閉它們,當資源用盡時會產生運行時異常。這些異常很難調試,我們可能需要查看我們使用該類型資源的每個地方,以確保我們正在關閉它。因此,Java 7 的一項改進是 try-with-resources,我們可以在 try 語句中創建資源並在 try-catch 區塊中使用它。當執行離開 try-catch 區塊時,運行時環境會自動關閉這些資源。具有此改進的 try-catch 區塊示例如下:
try (MyResource mr = new MyResource()) {
System.out.println("MyResource created in try-with-resources");
} catch (Exception e) {
e.printStackTrace();
}
閱讀更多關於此的資訊,請參閱 Java 7 ARM。
6. Java 中 Checked 和 Unchecked Exceptions 有什麼區別?
- Checked Exceptions 應該在程式碼中使用 try-catch 區塊來處理,否則方法應該使用 throws 關鍵字通知呼叫者可能從該方法拋出的已檢查異常。Unchecked Exceptions 在程式中不需要處理,也不需要在方法的 throws 子句中提及它們。
Exception
是所有已檢查異常的超類,而RuntimeException
是所有未檢查異常的超類。請注意,RuntimeException 是 Exception 的子類。- 已檢查異常是需要在程式碼中處理的錯誤情況,否則會在編譯時發生錯誤。例如,如果使用 FileReader 讀取檔案,它會拋出 FileNotFoundException,我們必須在 try-catch 區塊中捕獲它,或者再次將其拋出給呼叫方法。未檢查異常大多是由於編程不當造成的,例如,在對象引用上調用方法時出現 NullPointerException,而沒有確保它不是 null。例如,我可以編寫一個方法來從字符串中刪除所有元音字母。呼叫者有責任確保不傳遞空字符串。我可能會更改方法來處理這些情況,但理想情況下,呼叫者應該負責處理此問題。
7. 在Java中,throw和throws关键字有什么区别?
throws关键字用于方法签名中声明方法可能抛出的异常,而throw关键字用于中断程序流程并将异常对象交给运行时处理。
8. 如何在Java中编写自定义异常?
我们可以扩展Exception
类或其任何子类来创建自定义异常类。自定义异常类可以拥有自己的变量和方法,我们可以使用这些变量和方法将错误代码或其他异常相关信息传递给异常处理程序。下面是一个简单的自定义异常示例。
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中的OutOfMemoryError是什么?
Java中的OutOfMemoryError是java.lang.VirtualMachineError的子类,当JVM耗尽堆内存时抛出。我们可以通过为java应用程序提供更多内存来修复此错误,通过java选项运行。 $>java MyProgram -Xms1024m -Xmx1024m -XX:PermSize=64M -XX:MaxPermSize=256m
10. 造成“Exception in thread main”异常的不同场景是什么?
一些常见的主线程异常场景包括:
- Exception in thread main java.lang.UnsupportedClassVersionError: 当您的java类是从另一个JDK版本编译的,并且您正试图从另一个java版本运行它时,会出现此异常。
- Exception in thread main java.lang.NoClassDefFoundError: 有两种情况下会出现此异常。第一种是您使用.class扩展名提供了类的全名。第二种情况是类找不到。
- Exception in thread main java.lang.NoSuchMethodError: main: 当您尝试运行不具有主方法的类时会出现此异常。
- 主线程中的异常 java.lang.ArithmeticException:无论何时从主方法抛出异常,都会在控制台中打印异常。第一部分解释了异常是从主方法抛出的,第二部分打印异常类名,然后在冒号后打印异常消息。
更多信息请参阅Java主线程异常。
11. Java 中 final、finally 和 finalize 有什么区别?
final 和 finally 是 Java 中的关键字,而 finalize 是一个方法。final 关键字可用于类变量,以防止重新赋值,也可用于类以避免被其他类扩展,以及用于方法以避免被子类覆盖。finally 关键字与 try-catch 块一起使用,以提供一些语句,这些语句始终会执行,即使出现异常,通常 finally 用于关闭资源。finalize() 方法由垃圾收集器在对象销毁之前执行,这是确保所有全局资源都已关闭的好方法。在这三者中,只有 finally 与 Java 异常处理相关。
12. 當主方法拋出異常時會發生什麼?
當主()方法拋出異常時,Java Runtime 終止程序並在系統控制台中打印異常消息和堆棧跟蹤。
13. 我們可以有一個空的捕獲塊嗎?
我們可以有一個空的捕獲塊,但這是一個不良編程的示例。我們絕不能有一個空的捕獲塊,因為如果該塊捕獲到異常,我們將對異常沒有任何信息,並且將是一個夢魘來調試它。至少應該有一個日誌記錄語句來將異常詳細信息記錄在控制台或日誌文件中。
14. 提供一些 Java 異常處理的最佳實踐?
一些與 Java 異常處理相關的最佳實踐包括:
- 使用特定的異常來方便調試。
- 在程序中及早抛出异常(快速失败)。
- 在程序末尾捕获异常,让调用者处理异常。
- 使用Java 7的ARM特性确保资源被关闭,或者使用finally块来正确关闭它们。
- 始终记录异常消息以进行调试。
- 使用多重捕获块以获得更清晰的关闭操作。
- 使用自定义异常从您的应用程序API中抛出单一类型的异常。
- 遵循命名约定,异常类名总是以Exception结尾。
- 使用javadoc中的@throws来记录方法抛出的异常。
- 异常是昂贵的,因此仅在有意义时才抛出。否则,您可以捕获它们并提供空或空响应。
详细了解有关Java异常处理最佳实践的内容,请阅读Java异常处理最佳实践。
15. 以下程序有什么问题,我们该如何修复?
在本节中,我们将探讨一些与Java异常相关的编程问题。
-
以下程序有什么问题?
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{ } }
上面的程序无法编译,并显示错误消息为“异常FileNotFoundException已被IOException的替代捕获”。这是因为FileNotFoundException是IOException的子类,有两种解决此问题的方法。第一种方法是使用单个catch块捕获这两个异常。
try { testExceptions(); }catch(FileNotFoundException e){ e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); }
另一种方法是从多重捕获块中删除FileNotFoundException。
try { testExceptions(); }catch (IOException e) { e.printStackTrace(); }
您可以根据您的catch块代码选择任何一种方法。
-
以下程序有什麼問題?
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{ } }
該程序無法編譯,因為FileNotFoundException是IOException的子類,因此FileNotFoundException的捕獲塊是無法到達的,您將收到一條錯誤消息:“無法到達的FileNotFoundException的捕獲塊。 它已由IOException的捕獲塊處理”。您需要修正捕獲塊的順序來解決此問題。
try { go(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (JAXBException e) { e.printStackTrace(); }
請注意,JAXBException與IOException或FileNotFoundException無關,可以放在上述捕獲塊層次結構的任何位置。
-
以下程式有何問題?
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. This exception is never thrown from the try statement body」。要解決此問題,您必須刪除JAXBException的catch區塊。請注意,捕獲NullPointerException是有效的,因為它是一個未檢查的例外。
-
以下程式碼有什麼問題?
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{ } }
這是一個詭問題,程式碼沒有問題,它將成功編譯。我們始終可以捕獲一個Exception或任何未檢查的例外,即使它不在方法的throws子句中。同樣地,如果一個方法(foo)在throws子句中聲明了一個未檢查的例外,也不需要在程式中處理它。
-
以下程序有什么问题?
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()方法签名与父类不同。要解决此问题,我们可以将子类方法的签名更改为与父类完全相同,或者从子类方法中删除throws子句,如下所示。
@Override public void start(){ }
-
以下程序有什麼問題?
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{ } }
上述程序無法編譯,因為多重捕獲塊中的異常對象是final的,我們無法更改其值。您將在編譯時錯誤地獲得“無法分配多重捕獲塊的參數 e”。我們必須刪除對“e”分配為新異常對象的語句才能解決此錯誤。詳情請參閱Java 7多重捕獲塊。
這就是關於Java異常的面試問題,希望您會喜歡。我將在將來添加更多問題到列表中,請確保您收藏它以便將來使用。
Source:
https://www.digitalocean.com/community/tutorials/java-exception-interview-questions-and-answers