`

Java异常处理最佳实践

    博客分类:
  • Java
 
阅读更多

 

 

Java Exception Handling Tutorial with Examples and Best Practices

Pankaj July 25, 2013 Java

 
 

Exception is an error event that can happen during the execution of a program and disrupts its normal flow. Java provides a robust and object oriented way to handle exception scenarios, known as Java Exception Handling. We will look into following topics in this tutorial.

  1. Java Exception Handling Overview
  2. Exception Handling Keywords
  3. Exception Hierarchy
  4. Useful Exception Methods
  5. Java 7 Automatic Resource Management and Catch block improvements
  6. Creating Custom Exception Classes
  7. Exception Handling Best Practices

Java Exception Handling Overview

We don’t like exceptions but we always have to deal with them, great news is that Java Exception handling framework is very robust and easy to understand and use. Exception can arise from different kind of situations such as wrong data entered by user, hardware failure, network connection failure, Database server down etc. In this section, we will learn how exceptions are handled in java.

Java being an object oriented programming language, whenever an error occurs while executing a statement, creates an exception object and then the normal flow of the program halts and JRE tries to find someone that can handle the raised exception. The exception object contains a lot of debugging information such as method hierarchy, line number where the exception occurred, type of exception etc. When the exception occurs in a method, the process of creating the exception object and handing it over to runtime environment is called “throwing the exception”.

 

Once runtime receives the exception object, it tries to find the handler for the exception. Exception Handler is the block of code that can process the exception object. The logic to find the exception handler is simple – starting the search in the method where error occurred, if no appropriate handler found, then move to the caller method and so on. So if methods call stack is A->B->C and exception is raised in method C, then the search for appropriate handler will move from C->B->A. If appropriate exception handler is found, exception object is passed to the handler to process it. The handler is said to be “catching the exception”. If there are no appropriate exception handler found then program terminates printing information about the exception.

Note that Java Exception handling is a framework that is used to handle runtime errors only, compile time errors are not handled by exception handling framework.

We use specific keywords in java program to create an exception handler block, we will look into these keywords next.

Exception Handling Keywords

Java provides specific keywords for exception handling purposes, we will look after them first and then we will write a simple program showing how to use them for exception handling.

  1. throw – We know that if any exception occurs, an exception object is getting created and then Java runtime starts processing to handle them. Sometime we might want to generate exception explicitly in our code, for example in a user authentication program we should throw exception to client if the password is null. throw keyword is used to throw exception to the runtime to handle it.
  2. throws – When we are throwing any exception in a method and not handling it, then we need to usethrows keyword in method signature to let caller program know the exceptions that might be thrown by the method. The caller method might handle these exceptions or propagate it to it’s caller method using throws keyword. We can provide multiple exceptions in the throws clause and it can be used with main() method also.
  3. try-catch – We use try-catch block for exception handling in our code. try is the start of the block and catch is at the end of try block to handle the exceptions. We can have multiple catch blocks with a try and try-catch block can be nested also. catch block requires a parameter that should be of type Exception.
  4. finally – finally block is optional and can be used only with try-catch block. Since exception halts the process of execution, we might have some resources open that will not get closed, so we can use finally block. finally block gets executed always, whether exception occurred or not.

Let’s see a simple programing showing exception handling in java.

ExceptionHandling.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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");
        }
 
    }
 
}

Output of above program is:

1
2
3
4
5
6
7
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)

Notice that testException() method is throwing exception using throw keyword and method signature uses throws keyword to let caller know the type of exceptions it might throw. In main() method, I am handling exception using try-catch block in main() method and when I am not handling it, I am propagating it to runtime with throws clause in main method. Notice that testException(-10) never gets executed because of exception and then execution of finally block after try-catch block is executed. The printStackTrace() is one of the useful method in Exception class and used for debugging purpose.

  • We can’t have catch or finally clause without a try statement.
  • A try statement should have either catch block or finally block, it can have both blocks.
  • We can’t write any code between try-catch-finally block.
  • We can have multiple catch blocks with a single try statement.
  • try-catch blocks can be nested similar to if-else statements.
  • We can have only one finally block with a try-catch statement.

Exception Hierarchy

As stated earlier, when any exception is raised an exception object is getting created. Java Exceptions are hierarchical and inheritance is used to categorize different types of exceptions. Throwable is the parent class of Java Exceptions Hierarchy and it has two child objects – Error and Exception. Exceptions are further divided into checked exceptions and runtime exception.

  1. Errors: Errors are exceptional scenarios that are out of scope of application and it’s not possible to anticipate and recover from them, for example hardware failure, JVM crash or out of memory error. That’s why we have a separate hierarchy of errors and we should not try to handle these situations. Some of the common Errors are OutOfMemoryError and StackOverflowError.
  2. Checked Exceptions: Checked Exceptions are exceptional scenarios that we can anticipate in a program and try to recover from it, for example FileNotFoundException. We should catch this exception and provide useful message to user and log it properly for debugging purpose. Exception is the parent class of all Checked Exceptions and if we are throwing a checked exception, we must catch it in the same method or we have to propagate it to the caller using throws keyword.
  3. Runtime Exception: Runtime Exceptions are cause by bad programming, for example trying to retrieve an element from the Array. We should check the length of array first before trying to retrieve the element otherwise it might throw ArrayIndexOutOfBoundException at runtime. RuntimeException is the parent class of all runtime exceptions. If we are throwing any runtime exception in a method, it’s not required to specify them in the method signature throws clause. Runtime exceptions can be avoided with better programming.

exception-hierarchy

Useful Exception Methods

Exception and all of it’s subclasses doesn’t provide any specific methods and all of the methods are defined in the base class Throwable. The exception classes are created to specify different kind of exception scenarios so that we can easily identify the root cause and handle the exception according to it’s type. Throwable class implements Serializable interface for interoperability.

Some of the useful methods of Throwable class are;

  1. public String getMessage() – This method returns the message String of Throwable and the message can be provided while creating the exception through it’s constructor.
  2. public String getLocalizedMessage() – This method is provided so that subclasses can override it to provide locale specific message to the calling program. Throwable class implementation of this method simply use getMessage() method to return the exception message.
  3. public synchronized Throwable getCause() – This method returns the cause of the exception or null id the cause is unknown.
  4. public String toString() – This method returns the information about Throwable in String format, the returned String contains the name of Throwable class and localized message.
  5. public void printStackTrace() – This method prints the stack trace information to the standard error stream, this method is overloaded and we can pass PrintStream or PrintWriter as argument to write the stack trace information to the file or stream.

Java 7 Automatic Resource Management and Catch block improvements

If you are catching a lot of exceptions in a single try block, you will notice that catch block code looks very ugly and mostly consists of redundant code to log the error, keeping this in mind Java 7 one of the feature was improved catch block where we can catch multiple exceptions in a single catch block. The catch block with this feature looks like below:

1
2
3
4
catch(IOException | SQLException | Exception ex){
     logger.error(ex);
     throw new MyException(ex.getMessage());
}

There are some constraints such as the exception object is final and we can’t modify it inside the catch block, read full analysis at Java 7 Catch Block Improvements.

Most of the time, we use finally block just to close the resources and sometimes we forget to close them and get runtime exceptions when the resources are exhausted. These exceptions are hard to debug and we might need to look into each place where we are using that type of resource to make sure we are closing it. So java 7 one of the improvement was try-with-resources where we can create a resource in the try statement itself and use it inside the try-catch block. When the execution comes out of try-catch block, runtime environment automatically close these resources. Sample of try-catch block with this improvement is:

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

Read a detailed explanation of this feature at Java 7 Automatic Resource Management.

Creating Custom Exception Classes

Java provides a lot of exception classes for us to use but sometimes we may need to create our own custom exception classes to notify the caller about specific type of exception with appropriate message and any custom fields we want to introduce for tracking, such as error codes. For example, let’s say we write a method to process only text files, so we can provide caller with appropriate error code when some other type of file is sent as input.

Here is an example of custom exception class and showing it’s usage.

MyException.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
    }
     
 
}
CustomExceptionExample.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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");
            }
        }
    }
 
}

Notice that we can have a separate method to process different types of error codes that we get from different methods, some of them gets consumed because we might not want to notify user for that or some of them we will throw back to notify user for the problem.

Here I am extending Exception so that whenever this exception is being produced, it has to be handled in the method or returned to the caller program, if we extends RuntimeException, there is no need to specify it in the throws clause. This is a design decision but I always like checked exceptions because I know what exceptions I can get when calling any method and take appropriate action to handle them.

Exception Handling Best Practices

  1. Use Specific Exceptions – Base classes of Exception hierarchy doesn’t provide any useful information, thats why Java has so many exception classes, such as IOException with further sub-classes as FileNotFoundException, EOFException etc. We should always throw and catch specific exception classes so that caller will know the root cause of exception easily and process them. This makes debugging easy and helps client application to handle exceptions appropriately.
  2. Throw Early or Fail-Fast – We should try to throw exceptions as early as possible. Consider above processFile() method, if we pass null argument to this method we will get following exception.
    1
    2
    3
    4
    5
    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)

    While debugging we will have to look out at the stack trace carefully to identify the actual location of exception. If we change our implementation logic to check for these exceptions early as below;

    1
    2
    3
    4
    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
    }

    Then the exception stack trace will be like below that clearly shows where the exception has occurred with clear message.

    1
    2
    3
    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 – Since java enforces to either handle the checked exception or to declare it in method signature, sometimes developers tend to catch the exception and log the error. But this practice is harmful because the caller program doesn’t get any notification for the exception. We should catch exception only when we can handle it appropriately. For example, in above method I am throwing exception back to the caller method to handle it. The same method could be used by other applications that might want to process exception in a different manner. While implementing any feature, we should always throw exceptions back to the caller and let them decide how to handle it.
  4. Closing Resources – Since exceptions halt the processing of program, we should close all the resources in finally block or use Java 7 try-with-resources enhancement to let java runtime close it for you.
  5. Logging Exceptions – We should always log exception messages and while throwing exception provide clear message so that caller will know easily why the exception occurred. We should always avoid empty catch block that just consumes the exception and doesn’t provide any meaningful details of exception for debugging.
  6. Single catch block for multiple exceptions – Most of the times we log exception details and provide message to the user, in this case we should use java 7 feature for handling multiple exceptions in a single catch block. This approach will reduce our code size and it will look cleaner too.
  7. Using Custom Exceptions – It’s always better to define exception handling strategy at the design time and rather than throwing and catching multiple exceptions, we can create a custom exception with error code and caller program can handle these error codes. Its also a good idea to create a utility method to process different error codes and use it.
  8. Naming Conventions and Packaging – When you create your custom exception, make sure it ends with Exception so that it will be clear from name itself that it’s an exception. Also make sure to package them like it’s done in JDK, for example IOException is the base exception for all IO operations.
  9. Use Exceptions Judiciously – Exceptions are costly and sometimes it’s not required to throw exception at all and we can return a boolean variable to the caller program to indicate whether an operation was successful or not. This is helpful where the operation is optional and you don’t want your program to get stuck because it fails. For example, while updating the stock quotes in database from a third party webservice, we may want to avoid throwing exception if the connection fails.
  10. Document the Exceptions Thrown – Use javadoc @throws to clearly specify the exceptions thrown by the method, it’s very helpful when you are providing an interface to other applications to use.

That’s all for exception handling in java, I hope you liked it and learned something from it.

处理原则:1.使用checked和unchecked的大的原则是要考虑到对于这个异常,客户端是否有能力进行处理。2.不要让你要抛出的checked exception升级到较高的层次。例如,不要让SQLException延伸到业务层,因为业务层并不关心,也不能理解SQLException。两种处理方法:转变SQLException为另外一个checked exception,如果客户端并不需要恢复这种异常的话;转变SQLException为一个unchecked exception,如果客户端对这种异常无能为力的话;
分享到:
评论

相关推荐

    Java 异常处理的 9 个最佳实践

    Java 异常处理的 9 个最佳实践,在处理异常时可以参考。

    Java异常处理的最佳实践

    Java异常,大家都很熟悉。那么具体操作是怎么样的呢?下面从基础开始,带大家一块了解是怎么实践的。本文是关于Exception处理的一篇不错的文章,从Java Exception的概念介绍起,依次讲解了Exception的类型(Checked/...

    详解Java异常处理最佳实践及陷阱防范

    主要介绍了Java异常处理最佳实践及陷阱防范,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

    Java异常处理和最佳实践(含案例分析).pdf

    Java异常处理和最佳实践(含案例分析)

    Java 异常处理面试集锦及答案20道

    异常处理是Java编程中重要的概念和技巧之一,也是面试中常见的考点。...通过阅读本文,您将能够深入了解Java异常处理的原理、使用方法以及异常处理的最佳实践。不论您是准备面试还是希望加强对Java异常处理的知识掌握

    Java异常处理.md

    Java异常处理是编写健壮程序的关键方面之一。本资料将帮助您了解异常类层次结构、异常处理、抛出异常、自定义异常和最佳实践。 在Java中,异常被分为受检异常和非受检异常。受检异常需要在编译时进行处理,而非受检...

    Java编程异常处理最佳实践【推荐】

    主要介绍了Java编程异常处理最佳实践【推荐】,具有一定参考价值,需要的朋友可以了解下。

    Java编程中10个最佳的异常处理技巧

    主要介绍了Java编程中10个最佳的异常处理技巧,在本文中,将讨论Java异常处理最佳实践,这些Java最佳实践遵循标准的JDK库,和几个处理错误和异常的开源代码,这还是一个提供给java程序员编写健壮代码的便利手册,需要的...

    EventBus最佳实践:Java手撕实现ESB底层事件委托处理机制的思路,配套演示代码

    2、根据事件传播模型,如果发生异常断言则停止事件传播。 3、能够对组件串行执行的先后顺序进行编排。 4、有共性的处理机制可以进行封装,执行过程中视其为透明。 5、通过隐藏一些实现细节,使每一个关注点看起来都...

    java8集合源码分析-noah-bestPractices:Java业务最佳实践与踩坑

    Noah-Java最佳实践与踩坑 查看详细文章,可以参考我的博客 代码篇 01 使用了并发工具类库,线程安全就高枕无忧了吗?: 02 代码加锁:不要让“锁”事成为烦心事: 03 线程池:业务代码最常用也最容易犯错的组件: 04...

    java-best-practices:编码,设计和架构Java应用程序的最佳实践

    异常处理的最佳实践是什么? 建议何时选择不检查的异常? 什么时候使用标记界面? 为什么ENUMS对于可读代码很重要? 为什么要最小化可变性? 什么是函数式编程? 为什么要选择Builder Pattern来构建复杂的对象? 为...

    深入理解_Java_虚拟机 JVM_高级特性与最佳实践

    第2章 Java内存区域与内存溢出异常 / 24 2.1 概述 / 24 2.2 运行时数据区域 / 25 2.2.1 程序计数器 / 25 2.2.2 Java虚拟机栈 / 26 2.2.3 本地方法栈 / 27 2.2.4 Java堆 / 27 2.2.5 方法区 / 28 2.2.6 运行...

    NodeJS错误处理最佳实践1

    背景本文假设:你已经熟悉了JavaScript、Java、 Python、 C++ 或者类似的语言中异常的概念,而且你知道抛出异常和捕获异常是什么意思。你熟悉怎

    深入Java虚拟机(原书第2版).pdf【附光盘内容】

    《深入Java虚拟机(原书第2版)》,原书名《Inside the Java Virtual Machine,Second Edition》,作者:【美】Bill Venners,翻译:曹晓钢、蒋靖,出版社:机械工业出版社,ISBN:7111128052,出版日期:2003 年 9 ...

    【Java面试+Java学习指南】 一份涵盖大部分Java程序员所需要掌握的核心知识

    Java异常 解读Java中的回调 反射 泛型 枚举类 Java注解和最佳实践 JavaIO流 多线程 深入理解内部类 javac和javap Java8新特性终极指南 序列化和反序列化 继承、封装、多态的实现原理 容器 Java集合类总结 Java集合...

    数据结构算法Java实现。关于Java《数据结构算法》核心技术学习积累的例子,是初学者及核心技术巩固的最佳实践。.zip

    安全:Java提供了丰富的安全特性,如禁止指针运算、自动内存管理和异常处理机制,以减少程序错误和恶意攻击的可能性。 可移植性:Java字节码可以在所有安装了JVM的设备上执行,从服务器到嵌入式系统,再到移动...

    廖雪峰 Java 教程.doc

    廖雪峰 Java 教程 Java教程 Java快速入门 Java简介 安装JDK 第一个Java程序 ...Java代码助手 ...Java程序基础 ...最佳实践 单元测试 编写JUnit测试 使用Fixture 异常测试 条件测试 参数化测试

    Java工程师面试复习指南

    Java异常 解读Java中的回调 反射 泛型 枚举类 Java注解和最佳实践 JavaIO流 多线程 深入理解内部类 javac和javap Java8新特性终极指南 序列化和反序列化 继承封装多态的实现原理 集合类 Java集合类总结 Java集合详解...

    java面试手册及各类java书籍

    性能优化篇:分析Java应用的性能瓶颈,提供优化策略和最佳实践,帮助读者提升应用的性能。 面试题解析篇:精选大量Java面试题,并给出详细的解析和答案,帮助读者更好地应对面试挑战。 特色亮点: 权威性与实用性:...

    java学习书籍及建议.docx

    它详细介绍了Java编程中的有效实践和最佳实践,如何避免常见的错误和陷阱。 《深入理解Java虚拟机》:这本书是Java虚拟机编程的经典教材,适合中级和高级开发者。它详细介绍了Java虚拟机的原理和内部实现,以及如何...

Global site tag (gtag.js) - Google Analytics