Java 提供了一种健壮且面向对象的方法来处理称为 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:finally 块是可选的,只能与 try-catch 块一起使用。由于异常会暂停执行过程,因此我们可能会打开一些不会关闭的资源,因此可以使用 finally 块。无论是否发生异常,finally 块都会始终执行。
3.解释Java异常层次结构?
Java 异常是分层的,继承用于对不同类型的异常进行分类。Throwable
是 Java 异常层次结构的父类,它有两个子对象– Error
和Exception
。异常进一步分为检查异常和运行时异常。
Error是超出应用程序范围的特殊情况,无法预见并从中恢复,例如硬件故障,JVM 崩溃或内存不足错误。
Checked Exception 是我们可以在程序中预期并尝试从程序中恢复的异常情况,例如 FileNotFoundException。我们应该捕获该异常,并向用户提供有用的消息,并正确记录下来以进行调试。Exception
是所有 “检查的异常” 的父类。
Runtime Exception是由错误的编程引起的,例如,尝试从 Array 中检索元素。在尝试检索元素之前,我们应该首先检查数组的长度,否则它可能ArrayIndexOutOfBoundException
在运行时抛出。RuntimeException
是所有运行时异常的父类。
4.Java异常类的重要方法是什么?
Exception及其所有子类均未提供任何特定方法,并且所有方法均在基类 Throwable 中定义。
- String getMessage() –此方法返回 Throwable 消息字符串,并且可以在通过其构造函数创建异常时提供该消息。
-
String getLocalizedMessage() –提供此方法,以便子类可以重写它以向调用程序提供特定于语言环境的消息。此方法的 Throwable 类实现只需使用
getMessage()
方法即可返回异常消息。 - synchronized Throwable getCause() - 此方法返回异常原因或 null (原因未知)。
- String toString() –此方法以 String 格式返回有关 Throwable 的信息,返回的 String 包含 Throwable 类的名称和本地化消息。
- void printStackTrace() –此方法将堆栈跟踪信息打印到标准错误流,此方法已重载,我们可以传递 PrintStream 或 PrintWriter 作为参数,以将堆栈跟踪信息写入文件或流。
5.解释 Java 7 ARM Feature和多捕获块?
如果您在单个 try 块中捕获了很多异常,则您会注意到 catch 块代码看起来非常丑陋,并且主要由用于记录错误的冗余代码组成,请记住,Java 7 的功能之一就是多捕获块我们可以在单个 catch 块中捕获多个异常。具有此功能的 catch 块如下所示:
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();
}
6.Java中的 Checked 和 Unchecked 异常有什么区别?
1、检查异常应在代码中使用 try-catch 块进行处理,否则方法应使用 throws 关键字使调用者知道该方法可能抛出的检查异常。未经检查的异常不需要在程序中处理,也不需要在方法的 throws 子句中提及。
2.、Exception是所有Checked 异常的超类,而RuntimeException
是所有Unchecked 的异常的超类。请注意,RuntimeException 是 Exception 的子类。
3、Checked 异常是需要在代码中处理的错误方案,否则您将获得编译时错误。例如,如果您使用 FileReader 读取文件,则可能会抛出该文件FileNotFoundException
,我们必须将其在 try-catch 块中捕获,或再次将其抛出给调用方方法。Unchecked 异常通常是由不良的编程引起的,例如,在调用对象引用中的方法而不确保其不为 null 时,会引发 NullPointerException。例如,我可以编写一种方法来删除字符串中的所有元音。确保不传递空字符串对象是调用者的责任。我可能会更改处理这些情况的方法,但理想情况下,调用方应注意这一点。
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 堆内存不足时,它会被 JVM 抛出。我们可以通过修改 java 选项提供更多内存来解决此错误。
$>java MyProgram -Xms1024m -Xmx1024m -XX:PermSize=64M -XX:MaxPermSize=256m
10.有哪些不同的情况导致 “主线程异常”?
一些常见的主线程异常情况是:
- main 线程中的 java.lang.UnsupportedClassVersionError 异常:当您的 Java 类是从另一个 JDK 版本编译的,而您试图从另一个 Java 版本运行它时,将发生此异常。
- main 线程中的 java.lang.NoClassDefFoundError 异常:此异常有两种变体。第一个是您以. class 扩展名提供类全名的位置。第二种情况是找不到类时。
- main 线程中的 java.lang.NoSuchMethodError 异常:当您尝试运行不具有 main 方法的类时,将发生此异常。
- main 线程中的 java.lang.ArithmeticException 异常:每当从 main 方法抛出任何异常时,它都会打印控制台异常。第一部分说明从 main 方法抛出异常,第二部分打印异常类名称,然后在冒号后打印异常消息。
11.Java中的 final,finally 和 finalize 有什么区别?
final 和 finally 是 Java 中的关键字,而 finalize 是一种方法。
-
final 关键字可以与类变量一起使用,以使它们不能被重新分配; class 可以避免通过类进行扩展; final
关键字可以与方法避免被子类覆盖; -
finally 关键字可以与 try-catch 块一起使用,以提供将始终执行的语句即使出现某些异常,通常最终还是会用来关闭资源。
-
finalize()方法在对象被销毁之前由垃圾回收器执行,这是确保关闭所有全局资源的好方法。
在这三个中,只有finally 与 Java 异常处理有关。
12.当 main 方法抛出异常时会发生什么?
当 main()方法引发异常时,Java Runtime 将终止程序并在系统控制台中打印异常消息和堆栈跟踪。
13.我们可以有一个空的捕获块吗?
我们可以有一个空的 catch 块,但这是最糟糕的编程示例。我们永远不应该有空的 catch 块,因为如果异常被该块捕获,我们将没有有关该异常的信息,调试它将是一场噩梦。至少应该有一条日志记录语句,以将异常详细信息记录在控制台或日志文件中。
14.提供一些 Java 异常处理最佳实践吗?
与 Java 异常处理有关的一些最佳实践是:
- 捕获特定异常可以简化调试。
- 在程序中尽早抛出异常(Fast-Fast)。
- 在程序后期捕获异常,让调用者处理异常。
- 使用 Java 7 ARM 功能来确保资源被关闭,或者使用 finally 块来正确地关闭它们。
- 始终记录异常消息以进行调试。
- 使用多捕获块让代码更加清洁。
- 使用自定义异常可以从应用程序 API 中引发单一类型的异常。
- 遵循命名约定,始终以 Exception 结尾。
- 使用 javadoc 中的 @throws 记录由方法引发的异常。
- 异常的代价很高,因此仅在有意义时才抛出异常。否则,您可以捕获它们并返回null或不响应。
15.以下程序有什么问题,我们该如何解决?
在这里,我们将研究与 Java 异常相关的一些编程问题。
1). 下面的程序有什么问题?
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 块。
try {
testExceptions();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
另一种方法是从多捕获块中删除 FileNotFoundException。
try {
testExceptions();
}catch (IOException e) {
e.printStackTrace();
}
您可以根据情况选择任何一种方法。
2). 下面的程序有什么问题?
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 的 catch 块不可访问,并且您将收到错误消息 “ Unreachable catch block for FileNotFoundException. It is already handled by the catch block for IOException”。
您需要修复 catch 块顺序才能解决此问题。
try {
go();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JAXBException e) {
e.printStackTrace();
}
请注意,JAXBException 与 IOException 或 FileNotFoundException 不相关,可以放置在以上 catch 块层次结构中的任何位置。
3). 下面的程序有什么问题?
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()方法应抛出此异常以捕获调用方法。您将收到错误消息 “ JAXBException 无法访问的捕获块。不会从 try 语句主体中引发此异常。
要解决此问题,您将必须删除 JAXBException 的 catch 块。
注意,捕获 NullPointerException 是有效的,因为它是未经检查的异常。
4). 下面的程序有什么问题?
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 子句中声明未经检查的异常,则在程序中处理该异常不是强制性的。
5). 下面的程序有什么问题?
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(){
}
6). 下面的程序有什么问题?
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{
}
}
上面的程序无法编译,因为多捕获块中的异常对象是最终对象,我们无法更改其值。由于“无法分配多捕获块的参数e”,将导致编译时错误。
我们必须删除对新异常对象的“ e”分配以解决此错误。
“不积跬步,无以至千里”,希望未来的你能:有梦为马 随处可栖!加油,少年!
关注公众号:「Java 知己」,每天更新Java知识哦,期待你的到来!
- 发送「Group」,与 10 万程序员一起进步。
- 发送「面试」,领取BATJ面试资料、面试视频攻略。
- 发送「玩转算法」,领取《玩转算法》系列视频教程。
- 千万不要发送「1024」...
每日福利
网友评论