Java中的异常


一、异常的概念和作用

概念:
以下程序执行过程中发生了不正常的情况,这种不正常的情况叫做异常

public class ExceptionTest01 {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        int c = a / b;
        System.out.println("a / b == " + c);
    }
}

以上程序会在控制台输出下述错误信息,这个信息就是异常信息,是 JVM 打印的。
在这里插入图片描述
作用:
Java 是很完善的语言,提供了异常的处理方式。Java 会把异常信息打印输出到控制台,供程序员参考。程序员看到异常信息后,可以对程序进行修改,是程序更加健壮。

存在形式:
异常在 Java 中以类的形式存在,每一个异常类都可以创建异常对象。

二、异常的分类和继承结构

异常主要分为:错误、编译时异常(受控异常/受检异常)、运行时异常(非受控异常/未受检异常)

  • 错误:如果应用程序出现了 Error,那么将无法恢复,只能重新启动应用程序,最典型的 Error 的异常是 StackOverflowError。
  • 编译时异常:出现了这种异常必须预先进行处理,不处理 Java 程序将无法编译通过。
  • 运行时异常:此种异常可以不用显示的处理,例如被 0 除异常,Java 没有要求我们一定要处理。

在这里插入图片描述
编译时异常和运行时异常,都是发生在运行阶段

三、异常的两种处理方式

第一种方式:
在方法声明的位置上,使用 throws 关键字,抛给上一级。谁调用我,我就抛给谁。抛给上一级。

第二种方式:
使用 try…catch 语句进行异常的捕捉。这件事发生了,谁也不知道,因为我给抓住了。

例:我是某集团的一个销售员,因为我的失误,导致公司损失了1000元,“损失1000元”这可以看做是一个异常发生了。我有两种处理方式:

  • 第一种方式:我把这件事告诉我的领导。【异常上抛】
  • 第二种方式:我自己掏腰包把这个钱补上。【异常的捕捉】

注意:Java 中异常发生之后如果一直上抛,最终抛给了 main 方法,main 方法继续向上抛,抛给了调用者 JVM,JVM 知道这个异常发生,只有一个结果:终止java程序的执行。

1、在方法定义处采用 throws 声明异常,如果声明的异常为编译时异常,那么调用方法必须处理此异常,否则编译器报错。

public class ExceptionTest02 {
    public static void main(String[] args) {
        doSome();    // 此处编译器会报错,因为没有处理编译时异常
    }

	// ClassNotFoundException 直接父类是 Exception,属于编译时异常
    public static void doSome() throws ClassNotFoundException{  
        System.out.println("doSOme 执行");
    }
}

上述程序在 doSome() 处会报错,因为没有处理编译时异常。此时有两种处理方式:

①在 main 方法声明的位置上使用throws,继续上抛异常,抛给调用者:

    public static void main(String[] args) throws ClassNotFoundException {
        doSome();
    }

②try…catch 进行捕捉:

    public static void main(String[] args){
        try {
            doSome();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

2、只要异常没有被捕捉,采取上抛的话,此代码的后续代码不会执行。try 语句块中某一行代码出现异常,该行代码后面的代码不会执行,会跳到 catch 语句块中执行。try…catch结束后,后续的代码也可以执行。

public class ExceptionTest03 {
    public static void main(String[] args) {
        try {
            m1();   // 这条语句出现异常,捕捉后直接跳到 catch 中执行
            
            System.out.println("hello");		//永远不会执行
        }catch(Exception e) {
            System.out.println("文件路径有问题!");
        }
        
		System.out.println("结束了");          // 可以执行
    }

    public static void m1() throws FileNotFoundException{
        m2();
        System.out.println("m1执行");	// 永远不会执行
    }

    public static void m2() throws FileNotFoundException{
        new FileInputStream("......");	 // 源码中抛出了 FileNotFoundException 异常
        System.out.println("m2执行");	// 永远不会执行
    }
}

在这里插入图片描述
3、深入try…catch

  • catch 后面的小括号,可以写具体的异常类型,也可以写该异常类型的子类型。

  • catch 可以写多个,建议一个一个地处理,精确处理,这样有利于程序的调试。

  • catch 写多个的时候,从上到下的类型必须遵守从小到大。

  • JDK 8 中 catch 后面一个括号中可以写多个异常类型,用“ | ”符号分隔。

4、异常对象的两个方法

  • 取得异常描述信息:getMessage();

  • 取得异常的堆栈信息(比较适合于程序调试阶段):printStackTrace();

public class ExceptionTest04{
    public static void main(String[] args) {
        NullPointerException np = new NullPointerException("空指针异常");
        System.out.println(np.getMessage());
        System.out.println("--------");
        np.printStackTrace();			//	异步方式打印的
    }
}

在这里插入图片描述
5、try…catch…finally…

异常的捕获和处理需要采用 try 和 catch 来处理,具体格式如下:

try {

}catch(OneException e) {

}catch(TwoException e) {

}finally {

}

  • try 中包含了可能产生异常的代码。

  • try 后面是 catch,catch 可以有一个或多个,catch 中是需要捕获的异常。

  • 当 try 中的代码出现异常时,出现异常下面的代码不会执行,马上会跳转到相应的 catch 语句块中,如果没有异常不会跳转到 catch 中。

  • finally 表示,不管是否出现异常,finally 里的代码都执行,finally 和 catch可以分开使用,但 finally 必须和 try 一块使用,如 try { }finally { } 也是正确的。

finally 在任何情况下都会执行,通常在 finally 里关闭资源。

深入 finally:

public class ExceptionTest05 {
    public static void main(String[] args) {
        int a = 100;
        int b= 10;
        try {
            int c = a / b;
            System.out.println(c);

			// 以下两种情况取一种测试
            // return;      // 会执行 finally,顺序是先try,再执行finally,最后执行return
            System.exit(-1);    // 不会执行 finally,退出 Java 虚拟机了
        }catch(ArithmeticException ae) {
            ae.printStackTrace();
        }finally {
            //只有 java 虚拟机退出不会执行 finally,其他任何情况下都会执行 finally
            System.out.println("----------finally---------");
        }
    }

6、final、finally、finalize区别

  • final:是关键字,表示最终的、不可变的,被 final 修饰的类无法被继承,修饰的方法无法覆盖,修饰的变量不能重新赋值。

  • finally:是关键字,和 try 联合使用在异常处理机制中,finally 语句块中的语句一定会执行.

  • finalize:是标识符,是 Object 类中的一个方法名,这个方法是由垃圾回收器负责调用的。

四、自定义异常

Java 中自定义异常通常分两步:

  1. 写一个类继承 Exception 或 RuntimeException。

  2. 提供两个构造方法,一个无参的,一个有参带 String 参数的。

自定义异常通常继承于 Exception 还是 RuntimeException,应该看具体情况来定。

public class ExceptionTest06 {
    public static void main(String[] args) {
        try {
            method1(10, 0);
        }catch(MyException e) {
            System.out.println(e.getMessage());
        }
    }
    private static void method1(int value1, int value2) throws MyException {
        if (value2 == 0) {
        	// 创建异常对象并抛出
            throw new MyException("除数为 0");
        }
        int value3 = value1 / value2;
        System.out.println(value3);
    }
}

// 自定义异常类
class MyException extends Exception {
    public MyException() {
        //调用父类的默认构造函数
        super();
    }
    public MyException(String s) {
        //手动调用父类的构造方法
        super(s);
    }
}

在这里插入图片描述


参考资料

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值