Java中的异常处理

时间:2020-02-23 14:36:31  来源:igfitidea点击:

Java中的异常处理是一个非常有趣的主题。
异常是可能在程序执行期间发生的错误事件,它会破坏其正常流程。
Java提供了一种健壮且面向对象的方式来处理异常情况,称为Java异常处理。
我们将在本教程中研究以下主题。

Java中的异常处理

我们不喜欢异常,但是我们总是要处理它们,好消息是Java中的异常处理非常健壮,易于理解和使用。
Java中的异常可能源于不同类型的情况,例如用户输入的错误数据,硬件故障,网络连接故障,数据库服务器关闭等。
在本节中,我们将学习如何在Java中处理异常。

Java中的异常处理–概述

Java是一种面向对象的编程语言,只要在执行一条语句时发生错误,就会创建一个异常对象,然后程序的正常流程会停止,JRE会设法找到可以处理引发异常的人。
异常对象包含许多调试信息,例如方法层次结构,发生异常的行号,异常类型等。
当方法中发生异常时,将调用创建异常对象并将其移交给运行时环境的过程。
"抛出异常"。

运行时一旦接收到异常对象,它将尝试查找该异常的处理程序。
异常处理程序是可以处理异常对象的代码块。
查找异常处理程序的逻辑很简单–在发生错误的方法中开始搜索,如果找不到合适的处理程序,则转到调用者方法,依此类推。
因此,如果方法调用堆栈为A-> B-> C且方法C中引发了异常,则对适当处理程序的搜索将从C-> B-> A转移。
如果找到合适的异常处理程序,则将异常对象传递给该处理程序以对其进行处理。
据说处理程序正在"捕获异常"。
如果找不到合适的异常处理程序,则程序终止有关异常的打印信息。

请注意,Java异常处理是仅用于处理运行时错误的框架,java中的异常处理不处理编译时错误。

我们在Java程序中使用特定的关键字来创建异常处理程序块,接下来我们将研究这些关键字。

Java提供了用于异常处理目的的特定关键字,我们将首先照顾它们,然后编写一个简单的程序,展示如何使用它们进行异常处理。

Java异常处理关键字

让我们看一个简单的程序,显示Java中的异常处理。

  • Java中的异常处理–概述
  • Java异常处理关键字
  • Java异常层次结构
  • Java中的异常处理-有用的方法

上面程序的输出是:

package com.theitroad.exceptions;

import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionHandling {

	public static void main(String[] args) throws FileNotFoundException, IOException {
		try{
			testException(-5);
			testException(-10);
		}catch(FileNotFoundException e){
			e.printStackTrace();
		}catch(IOException e){
			e.printStackTrace();
		}finally{
			System.out.println("Releasing resources");			
		}
		testException(15);
	}
	
	public static void testException(int i) throws FileNotFoundException, IOException{
		if(i < 0){
			FileNotFoundException myException = new FileNotFoundException("Negative Integer "+i);
			throw myException;
		}else if(i > 10){
			throw new IOException("Only supported for index 0 to 10");
		}

	}

}

注意,testException()方法使用throw关键字引发异常,方法签名使用throws关键字使调用者知道它可能引发的异常类型。
在main()方法中,我正在使用main()方法中的try-catch块来处理异常,当我不处理它时,我将通过main方法中的throws子句将其传播到运行时。
注意,由于异常,testException(-10)永远不会执行,然后在try-catch块执行后执行finally块。
printStackTrace()是Exception类中的一种有用方法,用于调试目的。

java.io.FileNotFoundException: Negative Integer -5
	at com.theitroad.exceptions.ExceptionHandling.testException(ExceptionHandling.java:24)
	at com.theitroad.exceptions.ExceptionHandling.main(ExceptionHandling.java:10)
Releasing resources
Exception in thread "main" java.io.IOException: Only supported for index 0 to 10
	at com.theitroad.exceptions.ExceptionHandling.testException(ExceptionHandling.java:27)
	at com.theitroad.exceptions.ExceptionHandling.main(ExceptionHandling.java:19)

如前所述,当引发任何异常时,将创建一个异常对象。
Java异常是分层的,继承用于对不同类型的异常进行分类。
Throwable是Java异常层次结构的父类,它具有两个子对象–错误和异常。
异常进一步分为检查异常和运行时异常。

  • Java 7自动资源管理和捕获块改进

  • Java中的异常处理–创建自定义异常类

  • Java中的异常处理–最佳实践

  • throw –我们知道,如果发生任何异常,将创建一个异常对象,然后Java运行时开始处理以处理它们。
    有时我们可能想在代码中显式生成异常,例如,在用户身份验证程序中,如果密码为null,则应向客户端抛出异常。
    throw关键字用于向运行时抛出异常以对其进行处理。

  • throws –当我们在方法中抛出任何异常而不对其进行处理时,我们需要在方法签名中使用throws关键字,以使调用程序知道该方法可能抛出的异常。
    调用者方法可以处理这些异常,也可以使用throws关键字将其传播到其调用者方法。
    我们可以在throws子句中提供多个异常,它也可以与main()方法一起使用。

  • try-catch –我们在代码中使用try-catch块进行异常处理。
    try是块的开始,catch是try块的末尾,用于处理异常。
    我们可以使用try捕获多个catch块,并且try-catch块也可以嵌套。
    catch块需要一个应为Exception类型的参数。

Java异常层次结构

Java Exception及其所有子类均未提供任何特定方法,并且所有方法均在Throwable基类中定义。
创建异常类以指定不同类型的异常情况,以便我们可以轻松识别根本原因并根据异常类型进行处理。
Throwable类实现Serializable接口以实现互操作性。

  • 最终–最终块是可选的,只能与try-catch块一起使用。
    由于异常会暂停执行过程,因此我们可能会打开一些不会关闭的资源,因此我们可以使用finally块。
    无论是否发生异常,finally块都会始终执行。

  • 没有try语句,我们将没有catch或者finally子句。

  • 一个try语句应该具有catch块或者finally块,它可以同时具有两个块。

Java中的异常处理-有用的方法

Throwable类的一些有用方法是;

如果您在单个try块中捕获了很多异常,则您会注意到catch块代码看起来非常丑陋,并且主要由用于记录错误的冗余代码组成,请记住,Java 7的功能之一是改进了catch块,我们可以在单个catch块中捕获多个异常。
具有此功能的catch块如下所示:

  • 在try-catch-finally块之间,我们无法编写任何代码。

  • 一个try语句可以包含多个catch块。

  • try-catch块可以类似于if-else语句进行嵌套。

  • 我们只有一个带有try-catch语句的finally块。

  • 错误:错误是应用程序范围之外的特殊情况,无法预测并从中恢复,例如硬件故障,JVM崩溃或者内存不足错误。
    这就是为什么我们有一个单独的错误层次结构,我们不应该尝试处理这些情况。
    一些常见的错误是OutOfMemoryError和StackOverflowError。

Java 7自动资源管理和捕获块改进

存在一些约束,例如异常对象是最终对象,我们无法在catch块内对其进行修改,请在Java 7 Catch Block Improvements中阅读完整的分析。

catch(IOException | SQLException ex){
   logger.error(ex);
   throw new MyException(ex.getMessage());
}

大多数情况下,我们使用finally块只是为了关闭资源,有时我们忘记关闭它们并在资源耗尽时获取运行时异常。
这些异常很难调试,我们可能需要调查使用该类型资源的每个位置,以确保将其关闭。
因此,java 7的改进之一是try-with-resources,我们可以在try语句本身中创建资源,并在try-catch块内使用它。
当执行从try-catch块执行时,运行时环境将自动关闭这些资源。
具有这种改进的try-catch块示例为:

在Java 7自动资源管理中阅读有关此功能的详细说明。

try (MyResource mr = new MyResource()) {
          System.out.println("MyResource created in try-with-resources");
      } catch (Exception e) {
          e.printStackTrace();
      }

Java提供了许多异常类供我们使用,但是有时我们可能需要创建自己的自定义异常类,以通过适当的消息以及我们要引入以进行跟踪的任何自定义字段(例如错误代码)来通知调用者有关特定类型的异常的信息。

例如,假设我们编写了一种仅处理文本文件的方法,因此当其他类型的文件作为输入发送时,我们可以为调用者提供适当的错误代码。

Java中的异常处理–创建自定义异常类

这是自定义异常类的示例,并显示了其用法。

请注意,我们可以有一个单独的方法来处理从不同方法中获取的不同类型的错误代码,其中一些被消耗掉是因为我们可能不想为此通知用户,或者其中一些我们将返回以通知用户有关的错误代码。
问题。

package com.theitroad.exceptions;

public class MyException extends Exception {

	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;
	}
	

}
package com.theitroad.exceptions;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class CustomExceptionExample {

	public static void main(String[] args) throws MyException {
		try {
			processFile("file.txt");
		} catch (MyException e) {
			processErrorCodes(e);
		}
	
	}

	private static void processErrorCodes(MyException e) throws MyException {
		switch(e.getErrorCode()){
		case "BAD_FILE_TYPE":
			System.out.println("Bad File Type, notify user");
			throw e;
		case "FILE_NOT_FOUND_EXCEPTION":
			System.out.println("File Not Found, notify user");
			throw e;
		case "FILE_CLOSE_EXCEPTION":
			System.out.println("File Close failed, just log it.");
			break;
		default:
			System.out.println("Unknown exception occured, lets log it for further debugging."+e.getMessage());
			e.printStackTrace();
		}
	}

	private static void processFile(String file) throws MyException {		
		InputStream fis = null;
		try {
			fis = new FileInputStream(file);
		} catch (FileNotFoundException e) {
			throw new MyException(e.getMessage(),"FILE_NOT_FOUND_EXCEPTION");
		}finally{
			try {
				if(fis !=null)fis.close();
			} catch (IOException e) {
				throw new MyException(e.getMessage(),"FILE_CLOSE_EXCEPTION");
			}
		}
	}

}

其中我扩展了Exception,以便无论何时生成此异常,都必须在方法中对其进行处理或者将其返回给调用程序,如果我们扩展RuntimeException,则无需在throws子句中指定它。
这是一个设计决策,但是我始终喜欢检查异常,因为我知道在调用任何方法并采取适当的操作来处理它们时可以得到哪些异常。

这就是Java中异常处理的全部,希望您喜欢它并从中学到一些东西。

Java中的异常处理–最佳实践

  • 受检查的异常:受检查的异常是我们可以在程序中预期并尝试从程序中恢复的异常情况,例如FileNotFoundException。
    我们应该捕获该异常,并向用户提供有用的消息,并正确记录下来以进行调试。
    Exception是所有Checked Exceptions的父类,如果要抛出一个Checked异常,则必须在同一方法中捕获它,否则必须使用throws关键字将其传播给调用方。

  • 运行时异常:运行时异常是由错误的编程引起的,例如,尝试从Array中检索元素。
    在尝试检索元素之前,我们应该先检查数组的长度,否则在运行时它可能会抛出" ArrayIndexOutOfBoundException"。
    RuntimeException是所有运行时异常的父类。
    如果我们在方法中引发任何运行时异常,则无需在方法签名throws子句中指定它们。
    更好的编程可以避免运行时异常。

  • public String getMessage()–此方法返回Throwable消息字符串,并且可以在通过其构造函数创建异常时提供该消息。

  • public String getLocalizedMessage()–提供此方法,以便子类可以重写它以向调用程序提供特定于语言环境的消息。
    此方法的可抛出类实现仅使用getMessage()方法即可返回异常消息。

  • 公共同步Throwable getCause()-此方法返回异常的原因,或者返回null id(原因未知)。

  • public String toString()–此方法以String格式返回有关Throwable的信息,返回的String包含Throwable类的名称和本地化消息。

  • public void printStackTrace()–此方法将堆栈跟踪信息打印到标准错误流,此方法已重载,我们可以传递PrintStream或者PrintWriter作为参数,以将堆栈跟踪信息写入文件或者流。

  • 使用特定的异常-异常层次结构的基类没有提供任何有用的信息,这就是Java具有如此多的异常类的原因,例如IOException以及其他子类,如FileNotFoundException,EOFException等。
    我们应该始终抛出并捕获特定的异常类,因此该调用者将很容易知道异常的根本原因并进行处理。
    这使调试变得容易,并帮助客户端应用程序适当地处理异常。

  • 提早或者失败-我们应尽早提起异常。
    考虑上面的processFile()方法,如果将null参数传递给此方法,则会得到以下异常。

在调试时,我们将必须仔细查看堆栈跟踪,以识别异常的实际位置。
如果我们更改实现逻辑以如下所述检查这些异常;
然后,异常堆栈跟踪将如下所示,以清晰的消息清楚地显示异常发生的位置。

  • 延迟捕获-由于Java强制处理已检查的异常或者在方法签名中声明它,因此有时开发人员倾向于捕获异常并记录错误。
    但是这种做法是有害的,因为调用程序不会收到有关该异常的任何通知。
    仅当我们可以适当地处理异常时,才应捕获异常。
    例如,在上述方法中,我将异常抛出给调用方方法以进行处理。
    可能希望以不同方式处理异常的其他应用程序可以使用相同的方法。
    在实现任何功能时,我们应始终将异常抛出给调用者,并让他们决定如何处理它。