Java异常

Java异常

异常通常都是从 Throwable 类派生出来的,而Throwable类是直接从 object 类继承而来。

异常分类

异常通常有四大类:

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

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

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

  1. 错误的类型转换
  2. 数组访问越界
  3. 访问null指针
  4. 算术异常

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

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

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

受检异常是编译器要求必须处理的异常,必须使用 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.

捕获多个异常

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 将永远不可能执行,并且编译器将会报错。

自定义异常

// 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。

实例:

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)

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值