Java常见异常及处理方式总结

一、概述

异常指不期而至的各种状况,它在程序运行的过程中发生。作为开发者,我们都希望自己写的代码 永远都不会出现 bug,然而现实告诉我们并没有这样的情景。如果用户在程序的使用过程中因为一些原因造成他的数据丢失,这个用户就可能不会再使用该程序了。所以,对于程序的错误以及外部环境能够对用户造成的影响,我们应当及时报告并且以适当的方式来处理这个错误。

之所以要处理异常,也是为了增强程序的鲁棒性。

异常都是从 Throwable 类派生出来的,而 Throwable 类是直接从 Object 类继承而来。你可以在 Java SE 官方 API 文档中获取更多关于它们的知识。

二、异常分类

异常通常有四类:

  • Error:系统内部错误,这类错误由系统进行处理,程序本身无需捕获处理。
  • Exception:可以处理的异常。
  • RuntimeException:可以捕获,也可以不捕获的异常。
  • 继承 Exception 的其他类:必须捕获,通常在 API 文档中会说明这些方法抛出哪些异常。

平时主要关注的异常是 Exception 下的异常,而 Exception 异常下又主要分为两大类异常,一个是派生于 RuntimeExcption 的异常,一个是除了 RuntimeExcption 体系之外的其他异常。

RuntimeExcption 异常(运行时异常)通常有以下几种:

  • 错误的类型转换
  • 数组访问越界
  • 访问 null 指针
  • 算术异常

一般来说,RuntimeException 都是代码逻辑出现问题。

非 RuntimeException(受检异常,Checked Exception)一般有:

  • 打开一个不存在的文件
  • 没有找到具有指定名称的类
  • 操作文件异常

受检异常是编译器要求必须处理的异常,必须使用 try catch 处理,或者使用 throw 抛出,交给上层调用者处理。

三、声明及抛出

throw 抛出异常

当程序运行时数据出现错误或者我们不希望发生的情况出现的话,可以通过抛出异常来处理。

异常抛出语法:

throw new 异常类();

新建 ThrowTest.java:

public class ThrowTest {

    public static void main(String[] args) {
        Integer a = 1;
        Integer b = null;
        //当a或者b为null时,抛出异常
        if (a == null || b == null) {
            throw new NullPointerException();
        } else {
            System.out.println(a + b);
        }
    }
}

运行:

Exception in thread "main" java.lang.NullPointerException
    at ThrowTest.main(ThrowTest.java:8)

throws 声明异常

throws 用于声明异常,表示该方法可能会抛出的异常。如果声明的异常中包括 checked 异常(受检异常),那么调用者必须捕获处理该异常或者使用 throws 继续向上抛出。throws 位于方法体前,多个异常之间使用 , 分割。

新建ThrowsTest.java:

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class ThrowsTest {

    public static void main(String[] args) throws FileNotFoundException {
        //由方法的调用者捕获异常或者继续向上抛出
        throwsTest();

    }

    public static void throwsTest() throws FileNotFoundException {
        new FileInputStream("/home/project/shiyanlou.file");
    }
}

编译运行:

Exception in thread "main" java.io.FileNotFoundException: /home/project/shiyanlou.file (系统找不到指定的路径。)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(FileInputStream.java:195)
    at java.io.FileInputStream.<init>(FileInputStream.java:138)
    at java.io.FileInputStream.<init>(FileInputStream.java:93)
    at ThrowsTest.throwsTest(ThrowsTest.java:13)
    at ThrowsTest.main(ThrowsTest.java:8)

四、捕获异常

通常抛出异常后,还需要将异常捕获。使用 try 和 catch 语句块来捕获异常,有时候还会用到 finally。

对于上述三个关键词所构成的语句块,try 语句块是必不可少的,catch 和 finally 语句块可以根据情况选择其一或者全选。你可以把可能发生错误或出现问题的语句放到 try 语句块中,将异常发生后要执行的语句放到 catch 语句块中,而 finally 语句块里面放置的语句,不管异常是否发生,它们都会被执行。

你可能想说,那我把所有有关的代码都放到 try 语句块中不就妥当了吗?可是你需要知道,捕获异常对于系统而言,其开销非常大,所以应尽量减少该语句块中放置的语句

新建 CatchException.java:

public class CatchException {
    public static void main(String[] args) {
        try {
            // 下面定义了一个try语句块

            System.out.println("I am try block.");

            Class<?> tempClass = Class.forName("");
            // 声明一个空的Class对象用于引发“类未发现异常”
            System.out.println("Bye! Try block.");

        } catch (ClassNotFoundException e) {
            // 下面定义了一个catch语句块
            System.out.println("I am catch block.");

            e.printStackTrace();
            //printStackTrace()的意义在于在命令行打印异常信息在程序中出错的位置及原因

            System.out.println("Goodbye! Catch block.");

        } finally {
            // 下面定义了一个finally语句块
            System.out.println("I am finally block.");
        }
    }
}

编译运行:

I am try block.
I am catch block.
java.lang.ClassNotFoundException:
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Unknown Source)
        at CatchException.main(CatchException.java:8)
Goodbye! Catch block.
I am finally block.

五、捕获多个异常

在一段代码中,可能会由于各种原因抛出多种不同的异常,而对于不同的异常,我们希望用不同的方式来处理它们,而不是笼统的使用同一个方式处理,在这种情况下,可以使用异常匹配,当匹配到对应的异常后,后面的异常将不再进行匹配。

新建源代码文件 MultipleCapturesDemo.java:

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class MultipleCapturesDemo {
    public static void main(String[] args) {
        try {
            new FileInputStream("");
        } catch (FileNotFoundException e) {
            System.out.println("IO 异常");
        } catch (Exception e) {
            System.out.println("发生异常");
        }
    }
}

编译运行:

IO 异常

在处理异常时,并不要求抛出的异常同 catch 所声明的异常完全匹配,子类的对象也可以匹配父类的处理程序。比如异常 A 继承于异常 B,那么在处理多个异常时,一定要将异常 A 放在异常 B 之前捕获,如果将异常 B 放在异常 A 之前,那么将永远匹配到异常 B,异常 A 将永远不可能执行,并且编译器将会报错。

六、自定义异常

尽管 Java SE 的 API 已经为我们提供了数十种异常类,然而在实际的开发过程中,你仍然可能遇到未知的异常情况。此时,你就需要对异常类进行自定义。

自定义一个异常类非常简单,只需要让它继承 Exception 或其子类就行。在自定义异常类的时候,建议同时提供无参构造方法和带字符串参数的构造方法,后者可以为你在调试时提供更加详细的信息。

百闻不如一见,下面我们尝试自定义一个算术异常类。

创建一个 MyAriException 类:

主要的代码如下:

// MyAriException.java
public class MyAriException extends ArithmeticException {
    //自定义异常类,该类继承自ArithmeticException

    public MyAriException() {

    }
    //实现默认的无参构造方法

    public MyAriException(String msg) {
        super(msg);
    }
    //实现可以自定义输出信息的构造方法,将待输出信息作为参数传入即可
}

添加一个 ExceptionTest 类作为测试用,在该类的 main() 方法中,可以尝试使用 throw 抛出自定义的异常。

代码片段如下:

// ExceptionTest.java
import java.util.Arrays;

public class ExceptionTest {
    public static void main(String[] args) {
        int[] array = new int[5];
        //声明一个长度为5的数组

        Arrays.fill(array, 5);
        //将数组中的所有元素赋值为5

        for (int i = 4; i > -1; i--) {
            //使用for循环逆序遍历整个数组,i每次递减

            if (i == 0) {
            // 如果i除以了0,就使用带异常信息的构造方法抛出异常

                throw new MyAriException("There is an exception occured.");
            }

            System.out.println("array[" + i + "] / " + i + " = " + array[i] / i);
            // 如果i没有除以0,就输出此结果
        }
    }
}

检查一下代码,编译并运行,期待中的自定义错误信息就展现在控制台中了:

array[4] / 4 = 1
array[3] / 3 = 1
array[2] / 2 = 2
array[1] / 1 = 5
Exception in thread "main" MyAriException: There is an exception occured.
    at ExceptionTest.main(ExceptionTest.java:17)

七、异常堆栈

当异常抛出后,我们可以通过异常堆栈追踪程序的运行轨迹,以便我们更好的 DEBUG。

新建一个 ExceptionStackTrace.java:

public class ExceptionStackTrace {
    private static void method1() {
        method2();
    }

    private static void method2() {
        throw new NullPointerException();
    }
    public static void main(String[] args) {
        try {
            method1();
        } catch (Exception e) {
            //打印堆栈轨迹
            e.printStackTrace();
        }
    }
}

编译运行:

java.lang.NullPointerException
    at ExceptionStackTrace.method2(ExceptionStackTrace.java:7)
    at ExceptionStackTrace.method1(ExceptionStackTrace.java:3)
    at ExceptionStackTrace.main(ExceptionStackTrace.java:11)

通过上面的异常堆栈轨迹,在对比我们方法的调用过程,可以得出异常信息中首先打印的是距离抛出异常最近的语句,接着是调用该方法的方法,一直到最开始被调用的方法。从下往上看,就可以得出程序运行的轨迹

到此这篇关于Java常见异常及处理方式总结的文章就介绍到这了,更多相关Java异常内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2021-06-09

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

前言 不管在我们的工作还是生活中,总会出现各种"错误",各种突发的"异常".无论我们做了多少准备,多少测试,这些异常总会在某个时间点出现,如果处理不当或是不及时,往往还会导致其他新的问题出现.所以我们要时刻注意这些陷阱以及需要一套"最佳实践"来建立起一个完善的异常处理机制. 异常分类 首先,这里我画了一个异常分类的结构图. 在JDK中,Throwable是所有异常的父类,其下分为"Error"和"Exception&

一文给你通俗易懂的讲解Java异常

什么是异常? 最简单的,看一个代码示例: public static void main(String[] args) { int a = 1; int b = 0; System.out.println(a / b); } 这段代码有什么问题?简单,除数不能为0对吧,我们打印输出: 显而易见,程序出问题了,不能正常执行了,这里出现了一些爆红的信息,这些就是异常提示,这就是Java中提供的异常机制,当你的程序存在问题的情况下,会给你打印输出一些信息,这个就叫做异常信息. 字面意思上去理解,所谓"

Java异常处理 如何跟踪异常的传播路径

当程序中出现异常时,JVM会依据方法调用顺序依次查找有关的错误处理程序. 可使用printStackTrace 和 getMessage方法了解异常发生的情况: printStackTrace:打印方法调用堆栈. 每个Throwable类的对象都有一个getMessage方法,它返回一个字串,这个字串是在Exception构造函数中传入的,通常让这一字串包含特定异常的相关信息. 示例程序 // UsingExceptions.java // Demonstrating the getMessag

非常全面的Java异常处理(全文干货,值得收藏)

一.初始Java异常 1.对异常的理解:异常:在Java语言中,将程序执行中发生的不正常情况称为"异常".(开发过程中的语法错误和逻辑错误不是异常) 2.Java程序在执行过程中所发生对异常事件可分为两类: Error:Java虚拟机无法解决的严重问题.如:JVM系统内部错误.资源耗尽等严重情况.比如:StackOverflowError和OOM.一般不编写针对性 的代码进行处理. Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理.例如

Java异常处理操作实例小结

本文实例讲述了Java异常处理操作.分享给大家供大家参考,具体如下: 一 异常处理的使用--能够捕获到异常 1 代码 public class DealException { public static void main( String[] args ) { try // 检查这个程序块的代码 { int arr[] = new int[5]; arr[10] = 7; // 在这里会出现异常 } catch( ArrayIndexOutOfBoundsException e ) { Syst

JAVA异常分类和处理解析

前言 如果,某个方法不能按照正常的途径完成任务,就可以通过另一个路径退出方法,在这种情况下会抛出一个封装了错误信息的对象,此时,这个方法会立刻退出且不会返回任何值,调用此方法的其他代码也无法继续执行,异常处理机制会将代码执行交给异常处理器. Throwable是JAVA语言中所有错误或异常的超类,下一层分为error或者Exception. Error: Error类是指系统的内部错误和资源耗尽的错误,应用程序不会抛出该类对象,如果出现这样的错误,应用程序会尽量让程序安全的终止. Excepti

JAVA异常处理机制之throws/throw使用情况的区别

JAVA中throw和throws的区别:https://www.cnblogs.com/xiohao/p/3547443.html 区别:(摘自上面的博客) 1.throws出现在方法函数头:而throw出现在函数体. 2.throws表示出现异常的一种可能性,并不一定会发生这些异常:throw则是抛出了异常,执行throw则一定抛出了某种异常. 3.两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用

深入理解java异常处理机制的原理和开发应用

Java异常处理机制其最主要的几个关键字:try.catch.finally.throw.throws,以及各种各样的Exception.本篇文章主要在基础的使用方法上,介绍了如何更加合理的使用异常机制. try-catch-finally try-catch-finally块的用法比较简单,使用频次也最高.try块中包含可能出现异常的语句(当然这是人为决定的,try理论上可以包含任何代码),catch块负责捕获可能出现的异常,finally负责执行必须执行的语句,这里的代码不论是否发生了异常,

Java异常处理机制try catch流程详解

在项目中遇到try...catch...语句,因为对Java异常处理机制的流程不是很清楚,导致对相关逻辑代码不理解.所以现在来总结Java异常处理机制的处理流程: 1.异常处理的机制如下:在方法中用 try... catch... 语句捕获并处理异常,catch 语句可以有多个,用来匹配多个不同类型的异常.对于处理不了的异常或者要转型的异常,在方法的声明处通过 throws 声明异常,通过throw语句拋出异常,即由上层的调用方法来处理该异常. try { 逻辑程序块 } catch(Excep

深入理解java异常处理机制及应用

1. 引子 try-catch-finally恐怕是大家再熟悉不过的语句了,而且感觉用起来也是很简单,逻辑上似乎也是很容易理解.不过,我亲自体验的"教训"告诉我,这个东西可不是想象中的那么简单.听话.不信?那你看看下面的代码,"猜猜"它执行后的结果会是什么?不要往后看答案.也不许执行代码看真正答案哦.如果你的答案是正确,那么这篇文章你就不用浪费时间看啦. package Test; public class TestException { public TestEx

java异常处理机制示例(java抛出异常、捕获、断言)

这是一个介绍基本异常处理的小例子,包括抛出,捕获,断言,日志. Java异常处理通过5个关键字try.catch.throw.throws.finally进行管理.基本过程是用try语句块包住要监视的语句,如果在try语句块内出现异常,则异常会被抛出,你的代码在catch语句块中可以捕获到这个异常并做处理;还有以部分系统生成的异常在Java运行时自动抛出.你也可以通过throws关键字在方法上声明该方法要抛出异常,然后在方法内部通过throw抛出异常对象. 复制代码 代码如下: package

浅谈java异常处理之空指针异常

听老师说,在以后的学习中大部分的异常都是空指针异常.所以抽点打游戏的时间来查询一下什么是空指针异常 一:空指针异常产生的主要原因如下: (1)当一个对象不存在时又调用其方法会产生异常obj.method() // obj对象不存在 (2)当访问或修改一个对象不存在的字段时会产生异常obj.method() // method方法不存在 (3)字符串变量未初始化: (4)接口类型的对象没有用具体的类初始化,比如: List lt:会报错 List lt = new ArrayList():则不会报

Java异常处理之try...catch...finally详解

异常处理机制已经成为判断一门编程语言是否成熟的标准之一,其对代码的健壮性有很大影响.一直以来异常处理使用不是很得心应手,今天对异常进行了较为深入的学习,这篇主要是对try-catch-finally的一个总结. 一.java继承体系 Java语言为异常处理提供了丰富的异常类,这些类之间有严格的继承关系.如图: 从图中我们可以看出,所有的类都是继承于Throwable这个父类,java将所有的非正常情况分为两种:Error(错误)和Exception(异常),Error错误一般是于虚拟机相关的问题

c++异常处理机制示例及详细讲解

这两天我写了一个测试c++异常处理机制的例子,感觉有很好的示范作用,在此贴出来,给c++异常处理的初学者入门.本文后附有c++异常的知识普及,有兴趣者也可以看看. 下面的代码直接贴到你的console工程中,可以运行调试看看效果,并分析c++的异常机制. 复制代码 代码如下: #include "stdafx.h" #include<stdlib.h> #include<crtdbg.h> #include <iostream> // 内存泄露检测机

全面了解Java反射机制

什么是反射 反射 (Reflection) 是Java的特征之一,它允许运行中的Java程序获取自身的信息,并且可以操作类或对象的内部属性. 通俗的来讲就是:通过反射机制,可以在运行时获得程序或程序集中每一个类型的成员和成员的信息. 注意这里的重点是:运行时,而不是编译时.我们常规情况下写的对象类型都是在编译期就确定下来的.而Java反射机制可以动态地创建对象并调用其属性,这样创建对象的方式便异常灵活了. 虽然通过反射可以动态的创建对象,增加了灵活性,但也不是什么地方都可用,还要考虑性能.编码量

剖析Java中的事件处理与异常处理机制

一.事件处理 其实,由事件处理这个名字自然就想到MFC中的消息响应机制,就我的体会,它们应该算是南桔北枳的情形吧,我怀疑Java中的事件处理这个"新瓶"应是装的MFC中的消息响应这个"旧酒".     所谓的"事件"即如键盘按键.鼠标点击等这类由动作或什么导致某个状态改变并需要对这个改变作相应响应的这类改变.我们可以将Java中的事件分为按钮.鼠标.键盘.窗口.其它事件这几大类.     事件处理模型  1.   基于继承的事件处理模型(JDK1