Java中的异常处理

Java中的异常处理

 

Java中的异常处理是一个非常有趣的主题。异常是在程序执行期间可能发生的错误事件,并且会中断其正常流程。Java提供了一种强大的面向对象的方法来处理异常场景,称为Java异常处理。我们将在本教程中研究以下主题。

 

Java中的异常处理

  1. Java中的异常处理 - 概述
  2. Java异常处理关键字
  3. Java异常层次结构
  4. Java中的异常处理 - 有用的方法
  5. Java 7自动资源管理和Catch块改进
  6. Java中的异常处理 - 创建自定义异常类
  7. Java中的异常处理 - 最佳实践

Java中的异常处理 - 概述

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

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

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

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

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

 

Java异常处理关键字

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

  1. throw - 我们知道如果发生任何异常,就会创建一个异常对象,然后Java运行时开始处理它们。有时我们可能希望在代码中显式生成异常,例如在用户身份验证程序中,如果密码为null,我们应该向客户端抛出异常。throw关键字用于向运行时抛出异常来处理它。
  2. throws - 当我们在方法中抛出任何异常而不处理它时,我们需要在方法签名中使用throws关键字让调用者程序知道该方法可能抛出的异常。调用方法可以使用throws关键字处理这些异常或将其传播给它的调用方法。我们可以在throws子句中提供多个异常,它也可以与main()方法一起使用。
  3. try-catch - 我们在代码中使用try-catch块进行异常处理。try是块的开始,catch是在try块的末尾处理异常。我们可以使用try有多个catch块,try-catch块也可以嵌套。catch块需要一个应该是Exception类型的参数。
  4. finally - finally块是可选的,只能用于try-catch块。由于异常会暂停执行过程,因此我们可能会打开一些不会关闭的资源,因此我们可以使用finally块。finally块总是被执行,无论是否发生异常。

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


package com.journaldev.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");
		}

	}

}

上述计划的输出是:


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

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

  • 没有try语句我们不能有catch或finally子句。
  • try语句应该有catch块或者finally块,它可以有两个块。
  • 我们不能在try-catch-finally块之间编写任何代码。
  • 我们可以使用一个try语句来拥有多个catch块。
  • try-catch块可以嵌套,类似于if-else语句。
  • 我们只能有一个带try-catch语句的finally块。

Java异常层次结构

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

  1. 错误:错误是超出应用程序范围的异常情况,无法预测并从中恢复,例如硬件故障,JVM崩溃或内存不足错误。这就是为什么我们有一个单独的错误层次结构,我们不应该尝试处理这些情况。一些常见的错误是OutOfMemoryError和StackOverflowError。
  2. Checked Exceptions:Checked Exceptions是我们可以在程序中预期并尝试从中恢复的特殊情况,例如FileNotFoundException。我们应该捕获此异常并向用户提供有用的消息并正确记录以进行调试。异常是所有Checked Exceptions的父类,如果我们抛出一个已检查的异常,我们必须在同一个方法中捕获它,或者我们必须使用throws关键字将它传播给调用者。
  3. 运行时异常:运行时异常是由错误的编程引起的,例如尝试从Array中检索元素。我们应该在尝试检索元素之前先检查数组的长度,否则它可能会ArrayIndexOutOfBoundException在运行时抛出。RuntimeException是所有运行时异常的父类。如果我们在方法中抛出任何运行时异常,则不需要在方法签名throws子句中指定它们。通过更好的编程可以避免运行时异常。

java异常层次结构,java中的异常处理,java异常处理

 

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

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

Throwable类的一些有用方法是;

  1. public String getMessage() - 此方法返回消息String of Throwable,并且可以在通过构造函数创建异常时提供消息。
  2. public String getLocalizedMessage() - 提供此方法,以便子类可以覆盖它以向调用程序提供特定于语言环境的消息。此方法的Throwable类实现只是使用getMessage()方法返回异常消息。
  3. public synchronized Throwable getCause() - 此方法返回异常的原因或null id,原因未知。
  4. public String toString() - 此方法以String格式返回有关Throwable的信息,返回的String包含Throwable类和本地化消息的名称。
  5. public void printStackTrace() - 此方法将堆栈跟踪信息打印到标准错误流,此方法已重载,我们可以将PrintStream或PrintWriter作为参数传递,以将堆栈跟踪信息写入文件或流。

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

如果你在一个try块中捕获了很多异常,你会发现catch块代码看起来非常难看,而且大多数都是由冗余代码来记录错误,记住Java 7的一个特性是改进了catch块的地方我们可以在一个catch块中捕获多个异常。具有此功能的catch块如下所示:


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

有一些约束,例如异常对象是final,我们无法在catch块内修改它,在Java 7 Catch Block Improvements中读取完整分析。

大多数情况下,我们使用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自动资源管理中阅读此功能的详细说明。

 

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

Java提供了许多异常类供我们使用,但有时我们可能需要创建自己的自定义异常类,以通过适当的消息和我们想要引入的任何自定义字段(如错误代码)通知调用者特定类型的异常。 。例如,假设我们编写了一个只处理文本文件的方法,因此当其他类型的文件作为输入发送时,我们可以为调用者提供适当的错误代码。

以下是自定义异常类的示例,并显示了它的用法。


package com.journaldev.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.journaldev.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中的异常处理 - 最佳实践

  1. 使用特定的异常 - 异常层次结构的基类没有提供任何有用的信息,这就是为什么Java有这么多的异常类,比如IOException以及其他子类如FileNotFoundException,EOFException等。我们应该总是抛出并捕获特定的异常类,以便调用者很容易知道异常的根本原因并处理它们。这使调试变得简单并帮助客户端应用程序适当地处理异常。
  2. 尽早投掷或失败 - 我们应该尽早抛出异常。考虑上面的processFile()方法,如果我们将null参数传递给此方法,我们将得到以下异常。
    
    Exception in thread "main" java.lang.NullPointerException
    	at java.io.FileInputStream.<init>(FileInputStream.java:134)
    	at java.io.FileInputStream.<init>(FileInputStream.java:97)
    	at com.journaldev.exceptions.CustomExceptionExample.processFile(CustomExceptionExample.java:42)
    	at com.journaldev.exceptions.CustomExceptionExample.main(CustomExceptionExample.java:12)
    

    在调试时,我们必须仔细查看堆栈跟踪以确定异常的实际位置。如果我们改变我们的实现逻辑,以便在下面检查这些异常;

    
    private static void processFile(String file) throws MyException {
    		if(file == null) throw new MyException("File name can't be null", "NULL_FILE_NAME");
    //further processing
    }
    

    然后,异常堆栈跟踪将如下所示,清楚地显示发生异常的位置以及清除消息。

    
    com.journaldev.exceptions.MyException: File name can't be null
    	at com.journaldev.exceptions.CustomExceptionExample.processFile(CustomExceptionExample.java:37)
    	at com.journaldev.exceptions.CustomExceptionExample.main(CustomExceptionExample.java:12)
    
  3. Catch Late - 由于java强制要么处理已检查的异常,要么在方法签名中声明它,有时开发人员倾向于捕获异常并记录错误。但这种做法是有害的,因为调用者程序没有得到任何异常通知。只有当我们能够妥善处理它时,我们才应该捕获异常。例如,在上面的方法中,我将异常抛回调用方法来处理它。其他可能希望以不同方式处理异常的应用程序可以使用相同的方法。在实现任何功能时,我们应该始终将异常抛回给调用者并让他们决定如何处理它。
  4. 关闭资源 - 由于异常会暂停程序的处理,我们应该关闭finally块中的所有资源,或者使用Java 7 try-with-resources增强功能让java运行时为您关闭它。
  5. 记录异常 - 我们应该始终记录异常消息,同时抛出异常提供明确的消息,以便调用者可以很容易地知道异常发生的原因。我们应该总是避免使用只占用异常的空catch块,并且不提供任何有意义的调试异常细节。
  6. 单个catch块用于多个异常 - 大多数情况下,我们记录异常详细信息并向用户提供消息,在这种情况下,我们应该使用java 7功能来处理单个catch块中的多个异常。这种方法将减少我们的代码大小,它也会看起来更干净。
  7. 使用自定义异常 - 在设计时定义异常处理策略总是更好,而不是抛出和捕获多个异常,我们可以使用错误代码创建自定义异常,并且调用程序可以处理这些错误代码。创建一个实用程序方法来处理不同的错误代码并使用它也是一个好主意。
  8. 命名约定和打包 - 当您创建自定义异常时,请确保它以Exception结束,以便从名称本身清除它是一个例外。还要确保像在JDK中一样打包它们,例如IOException是所有IO操作的基本异常。
  9. 明智地使用例外 - 异常是昂贵的,有时根本不需要抛出异常,我们可以向调用者程序返回一个布尔变量来指示操作是否成功。这在操作是可选的并且您不希望程序因为失败而卡住的情况下很有用。例如,在从第三方Web服务更新数据库中的股票报价时,如果连接失败,我们可能希望避免抛出异常。
  10. 记录@throws引发的异常 - 使用javadoc 清楚地指定方法抛出的异常,当您提供要使用的其他应用程序的接口时,它非常有用。

这就是java中的异常处理,我希望你喜欢它并从中学到一些东西。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值